Func<> 委托有啥了不起的?

Posted

技术标签:

【中文标题】Func<> 委托有啥了不起的?【英文标题】:What's so great about Func<> delegate?Func<> 委托有什么了不起的? 【发布时间】:2010-09-24 02:28:52 【问题描述】:

对不起,如果这是基本的,但我正在尝试使用 .Net 3.5。

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

例如:public delegate TResult MyFunc&lt;TResult&gt;() 和各种重载的组合...

这个想法是在我试图理解 Func 委托时出现的,并遇到了以下场景:

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

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

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

谢谢,

【问题讨论】:

【参考方案1】:

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

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

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

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

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

反论点:有时定义代理可能有助于更好地传达意图。例如System.Threading.ThreadStart 超过 System.Action。所以说到底是个判断。

【讨论】:

不,你的答案更好。我很高兴 Func/Action 在那里,它们通常就足够了——但我认为仍然有一些时候值得定义你自己的代表。一个很好的例子是谓词 - 当已经存在 Func 但它表明意图时,它并不是绝对必要的。 同样,您可能希望提供一个命名委托,可以 表示为 Func (或其他) - 调用它 MatchCounter 使意图更清晰.这是一个平衡的行为。使用 Func 的好处当然是你不需要查找委托...... 好吧,我故意把这部分从我的答案中排除了,但似乎需要包含它才能完成它。 我在自定义委托类型中看到的真正好处是它们可以具有自定义参数名称和自定义 XML 文档。因此,我在需要这个的复杂场景中使用自定义委托类型。如果委托的目的从使用它的方法中很清楚,我使用ActionFunc【参考方案2】:

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

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

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

【讨论】:

【参考方案3】:

除了马克思的正确答案:

值得注意的是 Func 的相关家族,Action 代表。同样,这些类型重载了类型参数的数量,但声明为返回 void。 如果您想在 .NET 2.0 项目中使用 Func/Action,但以后通过简单的方式升级,您可以从我的version comparison page 中剪切和粘贴声明。如果您在 System 命名空间中声明它们,那么您只需稍后删除声明即可升级 - 但是您将无法(轻松)在 .NET 3.5 中构建相同的代码没有 删除声明。

【讨论】:

【参考方案4】:

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

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

在编译时结构性的委托类型比较在此之后变成名义上的(加载、调用)。当您考虑“我亲爱的库将被所有人永远使用”时,这似乎没问题,但它不能扩展到更复杂的系统。 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);

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

【讨论】:

【参考方案5】:

我喜欢委托的一点是,它们让我可以在这样的方法中声明方法,当您想要重用一段代码但只需要在该方法中使用它时,这很方便。由于这里的目的是尽可能限制范围,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"));

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于Func<> 委托有啥了不起的?的主要内容,如果未能解决你的问题,请参考以下文章

C#内置委托

C#语言中泛型和委托的关系是啥,Func<int>是泛型还是委托?

C# Action<T;和 Func<T;委托

委托 Action和Func

Func 和 Action 委托

使用 func<> 委托时,我可以以任何方式更改签名吗?