FUNC<>使用基类作为参数

时间:2011-05-10 11:30:57

标签: c#

我有一堆方法要检查一些元数据,所有这些方法都有不同的参数,但参数都来自BaseClass

public void CheckMethod(Func<BaseClass, Object> func)
{
    // Check method metadata here
}

public Object MethodToCheck(DerivedClass foo)
{
    // Whatever...
}

public void Test()
{
    CheckMethod(MethodToCheck);
}

代码在CheckMetadata(MethodToCheck)上失败,因为MethodToCheck将DerivedClass作为参数而不是BaseClass。我尝试过使用泛型:

public void CheckMethod<T>(Func<T, Object> func)

...

CheckMethod<DerivedClass>(MethodToCheck);

我希望CheckMethod的调用尽可能少,并且只希望用CheckMethod(MethodToCheck)调用它。这可能吗?

3 个答案:

答案 0 :(得分:2)

这样的东西
public void CheckMethod<T>(Func<T, Object> func) where T : BaseClass

答案 1 :(得分:1)

Check out the MSDN page on covariance and contravariance。这篇文章似乎暗示你在以前的版本中无法做到这一点。

如果您遇到旧版本,我建议您浏览访问者模式。

答案 2 :(得分:0)

这似乎是C#语言设计师没有想到的。你(和我)正在寻找的是一个编译时间&#34;接口&#34;对于Funcs / Delegates,它们基本上可以用在集合中。

另一种提出问题的方法是如何限制传递数组的人:

new[] {
    new Func<MaxLengthAttribute, string>(a => "maxlength=" + a.Length),
    new Func<RequiredAttribute, string>(a => "required")
}

仅传递格式为Func<out Attribute, string>的Func?应该简单......不是吗?

没有。正如另一个答案所关联的那样,out关键字不允许输入参数,例如我们在此处使用的参数。

如果你必须在编译时拥有约束,你就会陷入类似Java的丑陋杂技:

public interface IHasMethod<T>
    where T : BaseClass
{
    void MethodToCheck<T>(T, object);
}

public class Class1 : IHasMethod<DerivedClass>
{
    public object MethodToCheck(DerivedClass d)
    {

    }
}

表示您的测试如下:

public void CheckMethod(IHasMethod methodHaver)
{
}

Eugh。同样,我的数组示例变为:new IHasMethod[]

如果您更喜欢在运行时受到保护/检查的更漂亮,更脆弱的代码,您的替代方案是Delegate。你的签名:

public void CheckMethod(Delegate func)

我的数组示例:

new Delegate[] ...

但是在这两种情况下你都处于危险之中,因为委托的格式在编译时是无限制的 - 现在你正在做你通常会遇到编译器的问题,在继续之前检查正确数量的参数及其类型。如果可以,请尝试仅对调试代码进行丑陋的检查,并且对方法的任何调用都会被提取并暴露给单元测试,因此即使您已经失去编译安全性,也可以使用自动测试安全性替换它。像:

#if DEBUG
        foreach(var attrTempl in attributeTemplates)
        {
            var templParams = attrTempl.Method.GetParameters();
            if (templParams.Length != 1)
                throw new Exception("Can't have " + templParams.Length + " params in AttributeTemplate Delegate, must be Func<out Attribute, string>");

            var type1 = templParams[0].ParameterType;
            var type2 = attrTempl.Method.ReturnType;

            if (!type1.IsSubclassOf(typeof(System.Attribute)))
                throw new Exception("Input parameter type " + type1.Name + " must inherit from System.Attribute");

            if (type2 != typeof(string))
                throw new Exception("Output parameter type " + type2.Name + " must be string");
        }
#endif

支持障碍的最后一个帮助:如果采用Delegate方法,您将转换如下代码:

Func<A, B> fn

对此:

Delegate fn

你可能在某些时候称Func为:

var b = fn(a);

现在会出现编译错误,因为编译器准确断言它不知道此Delegate是否接受任何参数。可爱。您可以通过以下方式将其过去运行:

var b = (B)fn.DynamicInvoke(a);

Related