3

我试图理解复杂的库(LinqToCodeDom),有很多 lambdas 和 delegates 等等。两个相同的代码(我认为)的工作方式不同。在这两种情况下,我都尝试传递一个 TypeA 对象数组。

作品:

Func(new TypeA[] { new TypeA("value") });

不工作:

TypeA [] v = new TypeA[] { new TypeA("value") }; 
Func(v);

Func 接受参数 object[]

当它不起作用时,它会在库深处的某个地方出现 Null 引用而崩溃。

更新完整行。也许它比 Func 调用更复杂:

CodeMemberMethod cm = cls.AddMethod(
    MemberAttributes.Public, 
    m.ReturnType, 
    paramsAndName,
    Emit.@stmt(() =>
        CodeDom.Call(CodeDom.VarRef("obj"), m.Name)( *** PLACE FOR PARAM HERE*** )
);
4

3 回答 3

1

两种方法调用在功能上是相同的。

在第一种情况下,C# 编译器将生成一个变量来保存与第二种情况等效的数组。

考虑以下 C# 代码(在 LinqPad 中):

void Main()
{
    CallFunc(new [] { new Foo() });

    var foos = new [] { new Foo() };
    CallFunc(foos);
}

public class Foo { }

void CallFunc(Foo[] foos) { }

生成的 IL:

IL_0001:  ldarg.0     // These first two lines load 1
IL_0002:  ldc.i4.1    // for the size of the array
IL_0003:  newarr      Foo // Create the array of type Foo with the size
IL_0008:  stloc.1     // Pops the array into a variable
IL_0009:  ldloc.1     // These next two lines load
IL_000A:  ldc.i4.0    // the first index (0) of the array
IL_000B:  newobj      Foo..ctor // Creates a new Foo
IL_0010:  stelem.ref  // Loads Foo into the array
IL_0011:  ldloc.1     // Loads the array onto the stack
IL_0012:  call        CallFunc // Calls the function
IL_0017:  nop         // Same thing repeats below with some extra variable loading
IL_0018:  ldc.i4.1    
IL_0019:  newarr      Foo
IL_001E:  stloc.1     
IL_001F:  ldloc.1     
IL_0020:  ldc.i4.0    
IL_0021:  newobj      Foo..ctor
IL_0026:  stelem.ref  
IL_0027:  ldloc.1     
IL_0028:  stloc.0     // Pops the array into foos
IL_0029:  ldarg.0     
IL_002A:  ldloc.0     // Loads the array from foos
IL_002B:  call        CallFunc

CallFunc:
IL_0000:  nop         
IL_0001:  ret         

Foo..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ret         

代码之间的区别在于加载和读取两条指令foos

此 IL 的等效 C# 代码:

var arrayLength = 0;
var foos = new Foo[arrayLength];
var firstIndex = 0;
var foo = new Foo();
foos[firstIndex] = foo;
CallFunc(foos);
于 2013-07-06T17:50:17.477 回答
1

编辑

在运行和调试之后我来到这个 - 不,我仍然不知道问题到底是什么但是 - 它与表达式有关,这个调用在执行时会生成两种不同类型的表达式

当你使用

    Func(new TypeA[] { new TypeA("value") });

它产生

    {() => Invoke(Call(VarRef("obj"), value(Demo.Program+<>c__DisplayClass1).m.Name),new [] {new TypeA("value")})}

当你使用

    TypeA [] v = new TypeA[] { new TypeA("value") }; 
    Func(v);

它产生

   {() => Invoke(Call(VarRef("obj"), value(Demo.Program+<>c__DisplayClass1).m.Name),value(Demo.Program+<>c__DisplayClass1).v)}

注意区别

   new [] {new TypeA("value")}

对比

   value(Demo.Program+<>c__DisplayClass1).v

其中 Demo 是命名空间的名称,Program 是类,而 v 是变量

编辑 2.2

为了解释更多,我制作了这个示例应用程序

using System;

using System.Linq.Expressions;

namespace TestXml
{
    public class MyClass
    {
        public string Value { get; set; }
        public MyClass(string value)
        {
            Value = value;
        }

        public override string ToString()
        {
            return Value;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyClass[] parameter = new MyClass[] { new MyClass("1") };
            execute(() => TestInput( new MyClass[] { new MyClass("1") }));
            execute(() => TestInput(parameter));
        }

        public static void TestInput(params object[] parameters)
        {
            if (parameters != null && parameters.Length > 0) Console.WriteLine(parameters.GetType().FullName);
        }

        public static void execute(Expression<Action> exp)
        {
            Console.WriteLine(exp);
        }

        public delegate void ParamsDelegate(params object[] param);
    }
}

输出

() => TestInput(new [] {new MyClass("1")})
() => TestInput(value(TestXml.Program+<>c__DisplayClass0).parameter)

编辑 3

要知道行为差异的原因,我在 MSDN 上提出了这个问题并得到了这个答案:

“执行(()=> TestInput(新MyClass[] {新MyClass(“1”)}));”

不捕获任何东西,lambda 表达式不使用任何变量。

“捕获指向参数变量的指针?”

我想你可以这么说。它不是真正的指针,变量参数存储在对象字段中而不是存储在堆栈中,您看到的 c__DisplayClass0 是编译器生成的用于保存参数变量的类

这导致说这主要是 LinqToCodeDom 如何评估表达式的错误,似乎他们没有处理这种情况。

于 2013-07-06T16:26:24.327 回答
0

您的两个代码示例之间没有区别,以及当您调用时

Func(new TypeA("value")); //the compiler will create an array for you because of params
于 2013-07-06T16:36:49.480 回答