代码之家  ›  专栏  ›  技术社区  ›  Kiril

一个容器中有多个泛型类型

  •  1
  • Kiril  · 技术社区  · 14 年前

    我正在看的答案是 this question 关于一个容器中的多个泛型类型,我不能真正让它工作:的属性 Metadata 类不可见,因为抽象类没有它们。以下是原始问题中代码的稍微修改版本:

    public abstract class Metadata
    {
    }
    
    public class Metadata<T> : Metadata
    {
        // Per Ben Voigt's comments, here are the rest of the properties:
        public NUM_PARAMS NumParams { get; set; }
        public FUNCTION_NAME Name { get; set; }
        public List<Type> ParamTypes { get; set; }
        public Type ReturnType { get; set; }
        //...C
        public T Function { get; set; }
        public Metadata(T function)
        {
            Function = function;
        }
    }
    
    List<Metadata> metadataObjects;
    metadataObjects.Add(new Metadata<Func<double,double>>(SomeFunction));
    metadataObjects.Add(new Metadata<Func<int,double>>(SomeOtherFunction));
    metadataObjects.Add(new Metadata<Func<double,int>>(AnotherFunction));
    
    foreach( Metadata md in metadataObjects)
    {
          var tmp = md.Function; // <-- Error: does not contain a definition for Function
    }
    

    准确的误差是:

    错误CS1061:“metadata”不 包含“函数”和“否”的定义 扩展方法“函数”接受 ‘metadata’类型的第一个参数 可以找到(您是否缺少 使用指令或程序集 参考文献?)

    我相信这是因为抽象类没有定义属性 Function 因此,整个努力是完全无用的。有什么方法可以让我们得到这些财产吗?

    更新

    基本的想法是我有一个基因程序使用 元数据 功能(或 MetaFunction s)为了用这些函数构造表达式树。元数据允许我将一个函数的返回与另一个函数的输入参数正确匹配…它基本上把我的功能变成了乐高积木,电脑可以以各种方式将它们结合起来。这些函数都在同一个“域”内,所以我不会有任何随机混合和匹配的问题。

    我正在储存 元数据 元功能 在一对字典里:

    • 一个函数名作为键。
    • 另一个以参数个数为键。

    不管怎么说,我只是尽量接近最初的问题…基本问题是相同的,不管我是否使用 List 或A Dictionary . 我还坚持使用.NET 3.5,暂时无法更新到.NET 4.0。

    3 回复  |  直到 10 年前
        1
  •  2
  •   Ben Voigt    14 年前

    你会怎么做 md.Function 如果你能读的话?您不能调用它,因为您不知道参数类型。使用C 4.0,您可以使用 dynamic ,例如 foreach (dynamic md in metadataObjects) 然后你就不需要 Metadata 抽象基类。如果您只想访问 Delegate ,可以将抽象基类更改为具有 Delegate Metadata { get; } 属性并在中显式实现它 Metadata<T> ,然后您可以访问,例如函数的名称。

        2
  •  1
  •   luke    14 年前

    我认为这里的主要问题是你试图解决 动态的 非常的问题 静态的 (但灵活)通用编程工具。所以我看到你有两条路要走。

    1. 沿着类型边界拆分所有集合,为您拥有的每种类型的函数创建不同的集合。这在您的情况下应该是可能的,因为您提前知道所有类型,所以您将知道要创建什么类型。
    2. 接受你试图解决的问题的动态特性,然后为工作使用正确的工具。据我所知,您希望能够存储一个“函数”列表,然后在运行时动态决定使用哪些参数调用哪些函数。在这种情况下,你只需要一个更好的模型。

    我会选择2。根据我的理解,我认为这将是一个更好的模型。

    public class Variable
    {
        public Type Type {get; protected set;}
        public Object Value {get;protected set;}
        public Variable(Object val)
        {
            Type = val.GetType();
            Value = val;
        }
        public Variable(Type t, Object val)
        {
            Type = t;
            Value = val;
        }
    }
    
    public class ComposableFunction
    {
        public NUM_PARAMS NumParams { get; protected set; }
        public FUNCTION_NAME Name { get; protected set; }
    
        //our function signature
        public List<Type> ParamTypes { get; protected set; }
        public Type ReturnType { get; protected set; }
    
        private Delegate Function { get; set; }
        public Metadata (Delegate function)
        {
            Function = function;
        }
        public bool CanCallWith(params Variable vars)
        {
            return CanCallWith(vars);
        }
        public bool CanCallWith(IEnumerable<Variable> vars)
        {
            using(var var_enum = vars.GetEnumerator())
            using(var sig_enum = ParamTypes.GetEnumerator())
            {
                bool more_vars = false;
                bool more_sig =false;
                while(   (more_sig = sig_enum.MoveNext()) 
                      && (more_vars = var_enum.MoveNext())
                      && sig_enum.Current.IsAssignableFrom(var_enum.Current.Type));
                if(more_sig || more_vars)
                    return false;
            }
            return true;
        }
    
        public Variable Invoke(params Variable vars)
        {
            return Invoke(vars);
        }
        public Variable Invoke(IEnumerable<Variable> vars)
        {
            return new Variable(ReturnType, Function.DynamicInvoke(vars.Select(v => v.Value)));
        }
    }
    

    所以现在我们有了一个很好的模型来满足您的需求,因为它不需要泛型类型参数,所以当您迭代 List<ComposableFunction> 或者什么。

        3
  •  0
  •   luke    14 年前

    你是对的,错误是因为列表认为它有一堆元数据对象,所以当你迭代它时,你会得到元数据引用,为了访问在子类中定义的属性,你需要确保对象 子类,然后进行转换。

    foreach( Metadata md in metadataObjects)
    {
          var tmp =((Metadata<Func<double,double>>)md).Function; // but this will obviously fail if the type is incorrect. 
    }
    

    因此,这里您实际上只是将一个明确的编译时错误与一个潜在的运行时错误进行交易(取决于您列表中的内容)。真正的问题是:您希望如何处理所有这些不同的函数委托包装器?您希望TMP变量的类型是什么?

    您也可以尝试这样的类型测试解决方案

    foreach( Metadata md in metadataObjects)
    {
        var dd_md = md as Metadata<Func<double,double>>;
        var id_md = md as Metadata<Func<int,double>>;
        var di_md = md as Metadata<Func<double,int>>;
        if(dd_md != null)
        {
           var tmp1 =dd_md.Function;
        }
        else if(id_md != null)
        {
           var tmp2 =id_md.Function;
        }
        else if(di_md != null)
        {
           var tmp3 =di_md.Function;
        }
        //etc....
    
    }
    

    这也可以是一个可行的解决方案,只要您确切地知道提前会有什么类型的解决方案,但是它很烦人而且容易出错。