【问题标题】:How to correctly add Action and Func<> delegate types to C#2.0?如何正确地将 Action 和 Func<> 委托类型添加到 C#2.0?
【发布时间】:2010-12-14 16:16:19
【问题描述】:

.NET framework 3.5 引入了非常方便的 Action 和 Func 预定义委托类型。有什么方法可以有条件地将此类委托添加到我的源代码中,以便它们可以为 .NET 2.0 和 .NET 3.5 编译而不会发生名称冲突?类似#if not defined(Action) public delegate void Action(); r #if TARGET_FRAMEWORK

【问题讨论】:

  • 没有 C# 3.5 - Action 和 Func 是 .Net 3.5 框架中的类型。 C# 3.0 有一些语法(例如 var 和 lambdas)来帮助使用这些类型。
  • 感谢您的评论,我已经编辑了一个问题来澄清它。

标签: c#


【解决方案1】:

试试这个:

打开您的项目,在解决方案资源管理器中右键单击您的项目,导航到“构建”选项卡,在条件编译符号文本框中添加CSHARP2

声明一个名为 CSharpExtensions.cs 的类:

namespace System
{
#if CSHARP2
    public delegate void Action();
    public delegate void Action<T>(T t);
    public delegate void Action<T, U>(T t, U u);
    public delegate void Action<T, U, V>(T t, U u, V v);
    public delegate TResult Func<TResult>();
    public delegate TResult Func<T, TResult>(T t);
    public delegate TResult Func<T, U, TResult>(T t, U u);
    public delegate TResult Func<T, U, V, TResult>(T t, U u, V v);
#endif
}

假设您在 3.5 下编译 没有定义 CSHARP2 符号,您应该能够按预期使用上面的函数:

using System; // includes our new delegates
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static IEnumerable<U> Map<T, U>(IEnumerable<T> input, Func<T, U> f)
        {
            foreach (T t in input)
            {
                yield return f(t);
            }
        }

        static void Main(string[] args)
        {
            int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            foreach(String s in Map<int, string>(numbers, delegate(int i) { return String.Format("{0}^2 = {1}", i, i*i); }))
            {
                Console.WriteLine(s);
            }
            Console.ReadLine();
        }
    }
}

【讨论】:

  • 这是 .NET 2.0,而不是 C# 2.0,因此将其命名为 CSHARP2 非常具有误导性。
【解决方案2】:

当您重新定义 Action 和 Func 委托时,它们将位于不同的命名空间中,因此不会与 .Net 3.5 中的任何内容发生冲突。它们将具有相同的简单名称、签名和用途,但完全限定名称不同。所以没有冲突,也不需要预处理器指令。

唯一需要注意的是,它们将与 .Net 3.5 中定义的 Action 和 Func 类型不同。它们的工作方式完全相同。它们只是不同的类型。例如,如果您想调用采用原始 Action 类型的 3.5 BCL 方法,这可能会给您带来麻烦,但由于您正在争取 2.0 兼容性,这是不应该发生的。

【讨论】:

  • 但是我需要为它们使用一个完整的限定名称,对吗?如果我同时使用 System;使用 MyDelegateTypes;如果使用 .NET 3.5 目标编译会导致名称冲突?还是不行?
  • 没有。 Action 和 Func 位于基本 System 命名空间中,但它们实际上是在 System.Core 程序集中定义的,您不应该加载它,因为这会导致 2.0 编译出现问题。
  • 我刚刚编译了一个只使用System的测试代码;并使用 MyDelegates;。它给出了关于 System.Action 和 MyDelegates.Action 之间名称冲突的编译错误:(
  • 啊,你的意思是 System.Core ASSEMBLY!是的,它有效!从引用中删除它后,所有编译都很好。非常感谢!
【解决方案3】:

如果您在 VS 中对 Action 或 Func 声明执行“转到定义”,您将看到如何声明它们。例如 Action 声明如下:

public delegate void Action<T>(T obj);

编辑:要检查 .NET 的版本,我认为没有任何预定义。但是,您可以为每个目标版本定义构建配置并在那里定义一个符号。

【讨论】:

  • 我知道。但是我需要一种方法来为 C#2.0 声明它们,所以如果我的代码是在 c#3.5 中编译的,那么不会有名称冲突。
【解决方案4】:

我找不到一个预定义的预处理器变量供您使用,但您可以在项目属性中轻松设置一个(在“构建”选项卡中)。当您切换编译所针对的 .NET 版本时,您可以在构建之前将该标志切换为您想要的任何内容。

【讨论】:

  • 是的,这听起来像是一种解决方案,如果没有找到更好的解决方案,我会使用它。但是诸如特定构建的特定预处理器变量之类的事情会导致构建过程复杂化。不是一件很好的事情:(。
【解决方案5】:

我使用它取得了巨大的成功。在为 .NET 2.0 构建项目时定义 FOR_DOTNET2。更好的是,您可以从 MSDN 复制摘要 cmets 并将它们转换为 XML 格式。

#if FOR_DOTNET2
namespace System
{
    public delegate void Action<T>(T arg);
}
#endif

【讨论】:

    【解决方案6】:

    您可以将它们作为本地添加到您需要它们的类中。

    public class MyClass
    {
        // These were not added until .Net 3.5
        public delegate void Action();
        public delegate TResult Func<TResult>();
        public delegate TResult Func<TResult,T>(T arg);
        public delegate TResult Func<TResult,T1, T2>(T1 arg1, T2 arg2);
    
        public void DoSomething(Func<int> callback) {
            ...
        }
    }
    

    【讨论】:

    • 将它们添加到所有类是很多代码:(。也许是一些 #if derective 存在只允许在它们尚未定义时定义它们?像#if not defined(Func) ... 或 #if COMPILER_VERSION
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-27
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 2018-03-12
    • 2011-06-24
    • 2019-01-26
    相关资源
    最近更新 更多