【问题标题】:Anonymous delegates don't seem to enforce type-checking匿名代表似乎不强制类型检查
【发布时间】:2010-02-23 01:03:54
【问题描述】:

我在下面整理了一个小代码示例(目前在 C# 3.5 中,但也想知道答案在 C# 4.0 中是否有任何不同)

我有三个简单的委托和三个简单的函数......这里没问题,一切都按预期编译,如果我不小心尝试将委托 A 与方法 B 等链接(参数数量错误),将无法编译。

我很难理解的是为什么匿名函数似乎很乐意链接到所有三个命名的委托

public class Foo
{
   public delegate void del_TestWithNoParams();
   public delegate void del_TestWithThreeInts(int x, int y, int z);
   public delegate void del_TestWithIntPtr(IntPtr ptr);

   public void DoStuff()
   {
      //All OK so Far ... Code will not compile if I mix these up
      del_TestWithNoParams d1 =
         TestWithNoParams; d1();
      del_TestWithThreeInts d2 =
         TestWithThreeInts; d2(2, 4, 8);
      del_TestWithIntPtr d3 =
         TestWithIntPtr; d3(new IntPtr(0x1234567));

      //Why do these compile
      del_TestWithNoParams d4_nocompile =
         delegate { Console.WriteLine("AnonymousDel d4"); };
      del_TestWithThreeInts d5_nocompile =
         delegate { Console.WriteLine("AnonymousDel d5"); };
      del_TestWithIntPtr d6_nocompile =
         delegate { Console.WriteLine("AnonymousDel d6"); };

      // Edit 1 goes here
   }

     public void TestWithNoParams()
     { Console.WriteLine("NoParams"); }
     public void TestWithThreeInts(int x, int y, int z)
     { Console.WriteLine("Ints: {0},{1},{2}", x, y, z); }
     public void TestWithIntPtr(IntPtr ptr)
     { Console.WriteLine("IntPtr: 0x{0:X8}", ptr.ToInt32()); }

 }

另外(只是为了给你一个完整的可运行的应用程序......)

static void Main(string[] args)
  {
     var f = new Foo();
     f.DoStuff();
     Console.WriteLine("Done"); Console.ReadLine();
  }

编辑 1:使用 Lambda 方法

 //This work as expected - and fail to build if I get the parameter-count wrong.
 del_TestWithNoParams d7 =
   (() => Console.WriteLine("Lambda NoParams"));
 del_TestWithThreeInts d8 =
   ((a, b, c) => Console.WriteLine("Lambda Ints: {0},{1},{2}", a, b, c));
 del_TestWithIntPtr d9 =
   ((ptr) => Console.WriteLine("Lambda IntPtr: 0x{0:X8}", ptr.ToInt32()));
 Test(d7, d8, d9);

简单的辅助函数:

private void Test(del_TestWithNoParams del_A, del_TestWithThreeInts del_B, del_TestWithIntPtr del_C)
{
   del_A();
   del_B(2, 4, 8);
   del_C(new IntPtr(0x1234567));
}

...您是否同意这是编写相同代码的更好方法???


编辑 #2 - 答案摘要

我意识到(无论我以哪种方式编写代码),生成的 IL 字节码仍然是类型安全的..

与 C# 中的许多东西一样,命名委托、匿名委托和 lambda 方法各有各的位置,在“代码可读性”、“代码编译器扩展”和适用性之间存在平衡对于正在编写的个人应用程序。

下面的回复帮助回答了这个问题,并表明编译器确实在做类似于下面的事情。

1 - 它不会让我犯这个错误

//del_TestWithIntPtr d_mistake_A =
//   delegate(int x,int y,int z) { Console.WriteLine(x + y + z); };

2 - “编译器推断类型”将委托(例如 d5_nocompile)扩展为

del_TestWithThreeInts d_clearer_3P =
delegate(int x, int y, int z) { Console.WriteLine(x + y + z); };

3 - 可能出错(仍然是有效代码)

del_TestWithThreeInts d_EasyToMakeMistake =
delegate { Console.WriteLine("Oops - forgot to do anything with params"); };
// (this is really :- delegate (int x, int y, int z) {...} )

4 - 但是,当重写为 lambda 表达式时,稍后(或向其他开发人员)查看代码时会稍微明显一些

del_TestWithThreeInts d_LessEasyToMakeMistake =
((x, y, z) => Console.WriteLine("Still POSSIBLE to make mistake, but more obvious"));

【问题讨论】:

    标签: c# delegates anonymous-function


    【解决方案1】:

    不,这会强制进行类型检查。如果在分配给委托时未为匿名函数提供参数,它将采用默认值。

    请参阅 C# 语言规范(第 6.5 节),它指出

    匿名方法表达式或 lambda 表达式被归类为 匿名函数(第 7.14 节)。这 表达式没有类型但是 可以隐式转换为 兼容的委托类型或表达式 树型。具体来说,代表 D型与匿名兼容 函数 F 提供:

    • 如果 F 包含匿名函数签名,则 D 和 F 的数量相同 参数。
    • 如果 F 不包含匿名函数签名,则 D 可能有零个或多个参数 任何类型,只要没有 D 的参数 有 out 参数修饰符。

    如果您编译源代码并在 Reflector 中打开它(在框架 1.1 设置中),您会看到编译器自动将默认参数分配给没有参数列表的匿名方法。

     del_TestWithNoParams d4_nocompile = (CS$<>9__CachedAnonymousMethodDelegate40 != null) ? CS$<>9__CachedAnonymousMethodDelegate40 : (CS$<>9__CachedAnonymousMethodDelegate40 = new del_TestWithNoParams(Program.<Main>b__27));
        del_TestWithThreeInts d5_nocompile = (CS$<>9__CachedAnonymousMethodDelegate41 != null) ? CS$<>9__CachedAnonymousMethodDelegate41 : (CS$<>9__CachedAnonymousMethodDelegate41 = new del_TestWithThreeInts(Program.<Main>b__28));
        del_TestWithIntPtr d6_nocompile = (CS$<>9__CachedAnonymousMethodDelegate42 != null) ? CS$<>9__CachedAnonymousMethodDelegate42 : (CS$<>9__CachedAnonymousMethodDelegate42 = new del_TestWithIntPtr(Program.<Main>b__29));
    

    而 b__28(委托 del_TestWithThreeInts 的方法)将是这样的

    [CompilerGenerated]
    private static void <Main>b__28(int, int, int)
    {
        Console.WriteLine("AnonymousDel d5");
    }
    

    【讨论】:

    • @steve,很高兴它有帮助... :)
    【解决方案2】:

    当使用匿名方法时,真正发生的是创建一个类,并为委托的每个参数定义一个属性。

    如果不传递参数值,则使用默认值。

    【讨论】:

    • 我忘记了匿名委托有多危险......您是否同意从编码的角度来看,使用等效的 lambda 会“更安全”(以防止意外的“错误输入”)表达式(根据编辑#1)
    • 我不明白你怎么会遇到类型安全问题,如果你使用不同类型的参数,代码将无法编译。 lambda 语法只是一个偏好问题,我认为它更短,我更喜欢它,但我看不到与错误输入相关的好处
    【解决方案3】:

    如果您不为使用 delegate 关键字创建的匿名方法指定参数,则编译器会自动推断参数,因此委托签名是什么并不重要

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-19
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多