我正在查看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'的定义,没有 扩展方法'功能'接受a “元数据”类型的第一个参数 可以找到(你错过了吗? 使用指令或程序集 引用?)
我相信这是因为抽象类没有定义属性Function
,因此整个工作完全没用。有没有办法让我们得到属性?
基本思想是我有一个使用Metadata
函数(或MetaFunction
s)的遗传程序,以构建具有这些函数的表达式树。元数据允许我正确地匹配来自一个函数的返回与另一个函数的输入参数...它基本上将我的函数转换为legos,并且计算机可以以各种方式组合它们。这些函数都在同一个“域”中,因此随机混合和匹配它们不会有任何问题。
我将Metadata
或MetaFunction
s存储到几个词典中:
无论如何,我只是尽可能地贴近原始问题......无论我使用List
还是Dictionary
,基本问题都是一样的。我也坚持使用.NET 3.5,我将无法在一段时间内更新到.NET 4.0。
答案 0 :(得分:2)
如果你能阅读它,你会对md.Function
做什么?您无法调用它,因为您不知道参数类型。使用C#4.0,您可以使用dynamic
,例如foreach (dynamic md in metadataObjects)
然后您不需要Metadata
抽象基类。如果您只想访问Delegate
的成员,可以将抽象基类更改为具有Delegate Metadata { get; }
属性并在Metadata<T>
中明确实现的接口,然后您可以访问例如{{1}}。函数的名称。
答案 1 :(得分:1)
我认为这里的主要问题是您正在尝试使用Generic Programming的 Static (但灵活)工具来解决非常动态问题。所以我看到两种方式让你去。
我会选择选项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>
或其他什么时,你应该能够访问它的所有功能。
答案 2 :(得分:0)
你是对的,错误是因为列表认为它有一堆元数据对象所以当你迭代它时你得到元数据引用,为了访问子类中定义的属性你需要确保对象实际上是那个子类然后进行强制转换。
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....
}
只要您确切地知道提前会有哪些类型,但它很烦人且容易出错,这也可能是一个可行的解决方案。