【问题标题】:c# Array of single parameter functions each with a different type of paramc# 单参数函数数组,每个函数都有不同类型的参数
【发布时间】:2013-05-16 20:47:52
【问题描述】:

这真的是一个两部分的问题。

1.如何将参数不同类型的函数数组存储在Array或IEnumerable中

是否可以编写如下代码:

Func<object, bool> fObj = (o) => false;
Func<Animal, bool> fAni = (a) => false;
Func<Cat, bool> fCat = (c) => true;
var funcArray = new Function1Array<bool>(fObj, fAni, fCat);

Function1Array&lt;T&gt; 采用 (params Func&lt;?, T&gt;[] funcs) 之类的东西,即使 c# 不允许通用通配符

2。假设问题 1 可以完成,是否可以检查一个对象以查看它是否与其中一个函数的参数类型相同,如果是,则转换为对象

例子:

Cat myCat = new Cat();
funcArray.invokeFirstMatch(myCat); // Invokes fCat(myCat)

object myCat2 = new Cat();          // Declared as object instead of Cat
funcArray.invokeFirstMatch(myCat2); // Invokes fCat(myCat2)

Dog myDog = new Dog();
funcArray.invokeFirstMatch(myDog); // Invokes fObj(myDog)

我的猜测是,如果不以某种方式包装每个函数,这两个问题都是不可能的,因为 Func&lt;Cat, bool&gt; 不是 Func&lt;object, bool&gt; 的子类型,这意味着没有直接的方法以这种方式存储函数数组。

【问题讨论】:

  • 你想要的听起来像是多态性。
  • @MikeBantegui,是的,它很接近,但我更新了 #2 中的示例以显示差异。即使myCat 被声明为object...,我也希望能够运行正确的函数。我想我想要的是可以在运行时更改的更动态的多态性。
  • 您考虑过使用 dlr 吗?
  • @Tejas,如果我错了,请纠正我,但 DLR 用于从 .Net 已支持的语言以外的语言导入功能,而不是用于修改 c# 功能。
  • 不,可以使用DLR实现动态调度。 blogs.msdn.com/b/curth/archive/2008/11/15/…

标签: arrays c#-4.0 generics casting


【解决方案1】:

你可以这样做:

public class Function1Array<TOut> : IEnumerable
{
    private readonly List<Delegate> funcs = new List<Delegate>();

    public void Add<T>(Func<T, TOut> f)
    {
        this.funcs.Add(f);
    }

    public TOut InvokeFirstMatch<T>(T arg)
    {
        Delegate first = this.funcs.FirstOrDefault(d => d.GetType().GetGenericArguments()[0] == typeof(T));
        if (first == null) throw new ArgumentException("No match");
        return ((Func<T, TOut>)first)(arg);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.funcs.GetEnumerator();
    }
}

您可以使用集合初始化器对其进行初始化:

var arr = new Function1Array<bool> { fObj, fAni, fCat };

请注意,这需要参数类型完全匹配,尽管您可以将提供给FirstOrDefault 的谓词更改为使用IsAssignableFrom

InvokeFirstMatch 的泛型参数意味着搜索是基于参数的静态类型完成的。您可以改用运行时类型:

public TOut InvokeFirstMatch(object arg)
{
    var argType = arg.GetType();
    Delegate first = this.funcs.FirstOrDefault(d => d.GetType().GetGenericArguments()[0] == argType);
    if (first == null) throw new ArgumentException("No match");
    return (TOut)first.DynamicInvoke(Convert.ChangeType(arg, argType));
}

【讨论】:

  • 为了让它按我预期的方式工作,我必须 (1)InvokeFirstMatch 中删除类型参数 &lt;T&gt; 并改为使用 Type T = arg.GetType();(2) 将返回行改为(TOut)first.DynamicInvoke(Convert.ChangeType(arg, T));
  • 我接受这个答案,因为它在技术上是我所要求的,而且它是更强大的方法,但我真正需要的是动态调度,正如@Tejas 在他的评论中建议的那样。
猜你喜欢
  • 2015-07-14
  • 1970-01-01
  • 2021-02-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-13
  • 2016-09-13
相关资源
最近更新 更多