Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/299.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用反射、链式方法和lambda表达式动态创建对象_C#_.net_Reflection_Lambda_Expression - Fatal编程技术网

C# 使用反射、链式方法和lambda表达式动态创建对象

C# 使用反射、链式方法和lambda表达式动态创建对象,c#,.net,reflection,lambda,expression,C#,.net,Reflection,Lambda,Expression,导言 我的应用程序使用方法链接实例化一个对象,因此它的生成和配置如下: var car = new Car("Ferrari").Doors(2).OtherProperties(x = x.Color("Red")); 问题 我需要在运行时动态生成这个对象——配置所需的链接方法将在运行时确定,因此所有内容都必须动态组装。我过去曾使用反射来创建简单的对象,比如新车(“Ferrari”,2,“Red”)-我对此很酷-但从来没有使用包含lambda表达式作为参数的链式方法-这两个因素真的让我卡住


导言

我的应用程序使用方法链接实例化一个对象,因此它的生成和配置如下:

var car = new Car("Ferrari").Doors(2).OtherProperties(x = x.Color("Red"));

问题

我需要在运行时动态生成这个对象——配置所需的链接方法将在运行时确定,因此所有内容都必须动态组装。我过去曾使用反射来创建简单的对象,比如
新车(“Ferrari”,2,“Red”)
-我对此很酷-但从来没有使用包含lambda表达式作为参数的链式方法-这两个因素真的让我卡住了。我已经研究过表达式树,相信这是创建动态表达式参数的解决方案的一部分,但我一直在试图找出如何将其与反射缝合在一起,以创建基本对象和其他链接方法


感谢和感谢

提前感谢您抽出时间来研究我的问题,以及您可能提供的任何指导或信息


更新:结论

非常感谢dasblinkenlight和Jon Skeet的回答。我选择了dasblinkenlight的答案,因为他的代码样本让我马上就可以运行了。对于方法链接,我在接受的答案中基本上使用了相同的循环方法,因此我不会重复该代码,但下面是我编写的代码,用于动态地将表达式树方法调用转换为动作委托,然后可以通过反射
Invoke()
执行,如dasblinkenlight的答案中所述。正如乔恩指出的,这确实是问题的症结所在

帮助器类来存储方法元数据

public struct Argument
    {
        public string TypeName;
        public object Value;
    }

public class ExpressionTreeMethodCall
{
    public string MethodName { get; set; }
    public IList<Argument> Arguments { get; set; }

    public ExpressionTreeMethodCall()
    {
        Arguments = new List<Argument>();
    }
}
但决不能使用包含lambda表达式作为参数的链式方法

好吧,链式方法才是关键。这只是多次使用反射的问题。连锁

foo.X().Y()
您需要:

  • 从声明的
    foo
  • 使用
    foo
    的值作为调用目标,通过反射调用该方法,并记住结果(例如
    tmp
  • X
    的返回类型的声明类型中获取方法
    Y
    (请参见
    MethodInfo.ReturnType
  • 使用前面的结果(
    tmp
    )作为调用目标,通过反射调用该方法

lambda表达式更难-这实际上取决于如何首先提供表达式。构建一个委托以执行一个合理的任意表达式并不难,使用并调用并不难,但您需要知道自己在做什么。

您面临两个不同的问题:

  • 调用链式方法,以及
  • 调用将lambdas作为参数的方法
让我们分别处理这两个问题

假设您有以下可用信息:

  • 表示链中第一项(构造函数)的
    ConstructorInfo
  • 表示构造函数参数的对象数组
  • MethodInfo
    对象的数组-每个链接函数一个
  • 表示每个链接函数参数的对象数组数组数组
然后构建结果的过程如下所示:

ConstructorInfo constr = ...
object[] constrArgs = ...
MethodInfo[] chainedMethods = ...
object[][] chainedArgs = ...
object res = constr.Invoke(constrArgs);
for (int i = 0 ; i != chainedMethods.Length ; i++) {
    // The chaining magic happens here:
    res = chainedMethods[i].Invoke(res, chainedArgs[i]);
}
Expression arg = Expression.Parameter(typeof(MyCarType));
Expression red = Expression.Constant("Red");
MethodInfo color = typeof(MyCarType).GetMethod("Color");
Expression call = Expression.Call(arg, color, new[] {red});
var lambda = Expression.Lambda(call, new[] {arg});
Action<MyCarType> makeRed = (Action<MyCarType>)lambda.Compile();
循环结束后,您的
res
将包含一个已配置的对象

上面的代码假设链式方法中没有泛型方法;如果某些方法碰巧是泛型的,那么在调用
Invoke
之前,您需要另外一个步骤来创建泛型方法的可调用实例

现在让我们看看兰博达斯。根据传递给方法的lambda的类型,您需要传递具有特定签名的委托。您应该能够使用类将方法转换为可调用委托。您可能需要创建实现所需委托的支持方法。如果没有看到您必须能够通过反射调用的确切方法,很难说如何调用。您可能需要查找表达式树,并在编译它们之后获取
Func
实例。调用
x.Color(“Red”)
如下所示:

ConstructorInfo constr = ...
object[] constrArgs = ...
MethodInfo[] chainedMethods = ...
object[][] chainedArgs = ...
object res = constr.Invoke(constrArgs);
for (int i = 0 ; i != chainedMethods.Length ; i++) {
    // The chaining magic happens here:
    res = chainedMethods[i].Invoke(res, chainedArgs[i]);
}
Expression arg = Expression.Parameter(typeof(MyCarType));
Expression red = Expression.Constant("Red");
MethodInfo color = typeof(MyCarType).GetMethod("Color");
Expression call = Expression.Call(arg, color, new[] {red});
var lambda = Expression.Lambda(call, new[] {arg});
Action<MyCarType> makeRed = (Action<MyCarType>)lambda.Compile();
Expression arg=Expression.Parameter(typeof(MyCarType));
表达式红色=表达式常数(“红色”);
MethodInfo color=typeof(MyCarType).GetMethod(“color”);
Expression call=Expression.call(arg,color,new[]{red});
var lambda=Expression.lambda(调用,new[]{arg});
Action makeRed=(Action)lambda.Compile();

非常感谢。。因此,使用您提供的代码,“makeRed”操作对象是否可以存储在“chainedarg”中,并在链接循环期间的“Invoke()”调用期间执行?@mmacneil007绝对是这样
Action
是一个委托,它可以存储在
chainedArgs
数组中的一个数组中,并传递给相应的方法(在您的情况下,这将是
OtherProperties
)。很好,我相信这让我走上了正确的道路。我仍然在绕表达式树转,但我认为您在这里概述的方法应该可以做到这一点。再次感谢!