25

对不起,如果这是基本的,但我试图在 .Net 3.5 上学习。

问题: Func<> 有 5 个重载吗?从外观上看,我仍然可以自己创建一个类似的委托,MyFunc<> 具有确切的 5 个重载甚至更多。

例如:public delegate TResult MyFunc<TResult>()以及各种重载的组合......

当我试图理解 Func<> 代表并遇到以下情况时,这个想法出现了:

Func<int,int> myDelegate = (y) => IsComposite(10);

这意味着委托具有一个 int 类型的参数和一个 int 类型的返回类型。有五种变体(如果您通过智能感知查看重载)。所以我猜我们可以有一个没有返回类型的委托?

那么我是否有理由说 Func<> 没什么了不起,只是 .Net 框架中的一个示例,我们可以使用它,如果需要,创建自定义“func<>”代表以满足我们自己的需要?

谢谢,

4

6 回答 6

64

伟大之处在于建立共享语言以更好地沟通

不要为同一事物定义自己的委托类型(委托爆炸),而是使用框架提供的委托类型。任何阅读您的代码的人都会立即掌握您要完成的工作。. 最大限度地减少“这段代码实际上在做什么?”的时间。所以当我看到一个

  • Action = 一些只做某事但不返回输出的方法
  • 比较= 比较两个相同类型的对象并返回一个 int 表示顺序的方法
  • Converter = 将 Obj A 转换为等效的 Obj B
  • EventHandler = 对某个对象引发的事件的响应/处理程序,以事件参数的形式给出一些输入
  • Func = 一些方法,它接受一些参数,计算一些东西并返回一个结果
  • 谓词= 根据某些标准评估输入对象并将通过/失败状态返回为 bool

除非这是我直接关注的领域,否则我不必深入挖掘。因此,如果您觉得您需要的代表符合其中一项需求,请在推出自己的委托之前使用它们。

免责声明:我个人喜欢语言设计者的这一举动。

反论点:有时定义您的委托可能有助于更好地传达意图。例如System.Threading.ThreadStart结束System.Action。所以这最终是一个判断电话。

于 2008-11-26T06:44:39.220 回答
12

Func委托家族(及其无返回类型的表亲)Action并不比您在 .NET 框架中找到的任何其他东西都要大。它们只是为了重复使用,因此您不必重新定义它们。它们具有类型参数以保持通用性。例如, Func<T0,bool> 与 System.Predicate<T> 委托相同。它们最初是为 LINQ 设计的。

您应该能够将内置Func委托用于任何接受最多 4 个参数的值返回方法,而不是为此目的定义您自己的委托,除非您希望名称反映您的意图,这很酷。

您绝对需要定义委托类型的情况包括接受超过 4 个参数的方法、带有outrefparams参数的方法,或递归方法签名(例如,delegate Foo Foo(Foo f))。

于 2008-11-26T05:09:03.797 回答
9

除了马克思达的正确答案:

  • 值得一提的是 Func 的相关家族,即Action代表。同样,这些类型被类型参数的数量重载,但声明为返回 void。
  • 如果您想在 .NET 2.0 项目中使用 Func/Action,但以后有一个简单的升级途径,您可以从我的版本比较页面剪切和粘贴声明。如果您在命名空间中声明它们,那么您稍后只需删除声明即可升级 - 但是如果System删除声明,您将无法(轻松)在 .NET 3.5 中构建相同的代码。
于 2008-11-26T06:34:47.260 回答
7

解耦依赖和邪恶的捆绑是让它变得伟大的一件独特的事情。其他所有可以辩论并声称以某种本土方式可行的事情。

我一直在用一个旧的和沉重的库重构稍微复杂的系统,并且因为无法打破编译时间依赖性而被阻止 - 因为潜伏在“另一边”的命名委托。所有程序集加载和反射都没有帮助 - 编译器会拒绝将委托() {...} 转换为对象,而您为安抚它所做的任何事情都会在另一边失败。

编译时结构性的委托类型比较在此之后变成名义上的(加载、调用)。当您考虑“我亲爱的库将被所有人永远使用”时,这似乎还可以,但它不能扩展到稍微复杂一点的系统。Fun<> 模板将一定程度的结构等价带回了名义类型的世界。这是您通过推出自己的方式无法实现的方面。

示例 - 转换:

class Session ( 
    public delegate string CleanBody();    // tying you up and you don't see it :-)
    public static void Execute(string name, string q, CleanBody body) ... 

至:

    public static void Execute(string name, string q, Func<string> body)

允许完全独立的代码进行反射调用,例如:

Type type = Type.GetType("Bla.Session, FooSessionDll", true); 
MethodInfo methodInfo = type.GetMethod("Execute"); 

Func<string> d = delegate() { .....}  // see Ma - no tie-ups :-)
Object [] params = { "foo", "bar", d};
methodInfo.Invoke("Trial Execution :-)", params);

现有代码没有注意到差异,新代码没有得到依赖 - 地球上的和平:-)

于 2010-06-24T23:39:24.343 回答
1

我喜欢委托的一件事是他们让我在这样的方法中声明方法,当您想重用一段代码但只需要在该方法中时,这很方便。由于这里的目的是尽可能地限制范围,所以 Func<> 派上用场了。

例如:

string FormatName(string pFirstName, string pLastName) {
    Func<string, string> MakeFirstUpper = (pText) => {
        return pText.Substring(0,1).ToUpper() + pText.Substring(1);
    };

    return MakeFirstUpper(pFirstName) + " " + MakeFirstUpper(pLastName);
}

当您可以使用推理时,它会更容易和更方便,如果您创建一个这样的辅助函数,您就可以做到:

Func<T, TReturn> Lambda<T, TReturn>(Func<T, TReturn> pFunc) {
    return pFunc;
}

现在我可以在没有 Func<> 的情况下重写我的函数:

string FormatName(string pFirstName, string pLastName) {
    var MakeFirstUpper = Lambda((string pText) => {
        return pText.Substring(0,1).ToUpper() + pText.Substring(1);
    });

    return MakeFirstUpper(pFirstName) + " " + MakeFirstUpper(pLastName);
}

这是测试该方法的代码:

Console.WriteLine(FormatName("luis", "perez"));
于 2012-01-02T01:01:02.603 回答
-1

虽然这是一个旧线程,但我不得不添加 func<> 和 action<> 也帮助我们使用协方差和反方差。

http://msdn.microsoft.com/en-us/library/dd465122.aspx

于 2010-12-05T00:03:59.283 回答