我正在尝试理解复杂的库(LinqToCodeDom),有很多lambdas和委托等等。两个相同的代码(在我看来)的工作方式不同。 在这两种情况下,我都尝试传递一个TypeA对象数组。
使用:
Func(new TypeA[] { new TypeA("value") });
不起作用:
TypeA [] v = new TypeA[] { new TypeA("value") };
Func(v);
Func接受params对象[]
当它不起作用时,它会在lib的深度某处与Null引用崩溃。
更新完整行。也许它比Func调用更复杂:
CodeMemberMethod cm = cls.AddMethod(
MemberAttributes.Public,
m.ReturnType,
paramsAndName,
Emit.@stmt(() =>
CodeDom.Call(CodeDom.VarRef("obj"), m.Name)( *** PLACE FOR PARAM HERE*** )
);
答案 0 :(得分: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")}
VS
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
要知道行为不同的原因我问the question on MSDN并得到了这个答案:
“execute(()=&gt; TestInput(new MyClass [] {new MyClass(”1“)}));”
不捕获任何内容,lambda表达式不使用任何内容 变量
“捕获指向参数变量的指针?”
我想你可以这么说。它不是一个指针,变量 参数存储在对象字段中,而不是存储在 堆栈,你看到的c__DisplayClass0是由一个生成的类 编译器保存参数变量
这导致说这主要是LinqToCodeDom如何评估表达式的错误,似乎他们没有处理这种情况。
答案 1 :(得分: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);
答案 2 :(得分:0)
两个代码示例之间以及刚刚调用
时没有区别Func(new TypeA("value")); //the compiler will create an array for you because of params