【问题标题】:Multiple generic types in one container一个容器中的多个泛型类型
【发布时间】:2011-02-22 15:06:40
【问题描述】:

我正在查看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:“元数据”不 包含“功能”的定义并且没有 扩展方法 'Function' 接受 “元数据”类型的第一个参数 可以找到(您是否缺少 使用指令或程序集 参考?)

我相信这是因为抽象类没有定义属性Function,因此整个努力完全没有用。有没有办法获取属性?

更新

基本思想是我有一个遗传程序,它使用函数的Metadata(或MetaFunctions)来构建具有这些函数的表达式树。元数据允许我将一个函数的返回值与另一个函数的输入参数正确匹配……它基本上将我的函数变成了乐高积木,计算机可以以各种方式组合它们。这些函数都在同一个“域”内,所以我随机混合和匹配它们不会有任何问题。

我将MetadataMetaFunctions 存储到几个字典中:

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

在任何情况下,我只是尽量贴近原始问题...无论我使用List 还是Dictionary,基本问题都是相同的。我也被 .NET 3.5 卡住了,暂时无法更新到 .NET 4.0。

【问题讨论】:

  • 你提前知道你所有可能的函数类型吗?
  • @luke,是的,我愿意...我定义了它们中的每一个。
  • Bryan 在链接问题中的解决方案应该适用于您的情况。

标签: c# generics delegates containers abstract-class


【解决方案1】:

我认为这里的主要问题是您正在尝试使用非常静态(但灵活)的通用编程工具来解决非常动态 的问题。所以我看到你有两种方法。

  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&lt;ComposableFunction&gt; 或其他任何内容时访问它的所有功能。

【讨论】:

  • 这可能非常接近我认为适用于该项目的方法......这是一个相当复杂的想法,但你在这里给了我一些很好的指导,对我有帮助。
【解决方案2】:

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

【讨论】:

  • Ben Voigt,参数类型是元数据的一部分...我将更新代码,以便您查看它的外观。这是遗传程序的一部分,因此我为所有函数提供元数据,并允许程序根据元数据匹配函数。
  • 为此,我认为您根本不需要任何泛型。只需将属性公开为类型 Delegate,然后您就可以从 Delegate 中实际查询参数类型,而无需自己跟踪它们。而且,如果你有正确的参数,你可以调用 Invoke 来调用函数。
  • 我需要泛型,因为我正在对函数进行记忆:stackoverflow.com/questions/2852161/…
  • 我不明白您所说的:“将抽象基类更改为具有委托元数据 { get; } 属性并在元数据中显式实现它的接口”。 .. 特别是当你说它有“Delegate Metadata{ get; }”时,你能给我一些示例代码吗?
  • 我的意思是让属性“Delegate”的类型,所有委托类型的基类...msdn.microsoft.com/en-us/library/system.delegate.aspx将你的参数打包到一个数组中并调用msdn.microsoft.com/en-us/library/…你也可以查询参数类型等,通过检索“调用”方法或查询方法属性msdn.microsoft.com/en-us/library/…
【解决方案3】:

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

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....

}

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

【讨论】:

  • 我更新了我的问题并在元数据中添加了其余属性。基本思想是我有一个遗传程序,它使用函数的元数据来构建具有这些函数的表达式树。元数据允许我将一个函数的返回参数与另一个函数的输出参数正确匹配......它基本上将我的函数变成了乐高积木,计算机可以以各种方式组合它们。
  • 是的 - 执行 if/else 有点难看。我实际上有与每个可用函数的函数名称匹配的枚举,所以当我调用函数时我可以使用 switch 语句......我还拿着一个带有函数名称的字典,我想通过以函数名为键,获取元数据+函数。
猜你喜欢
  • 1970-01-01
  • 2021-10-31
  • 2022-06-15
  • 2014-08-02
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 2014-11-02
  • 1970-01-01
相关资源
最近更新 更多