我正在尝试为已经存在的对象调用反序列化构造函数。我如何使用表达式树来做到这一点?
我试过了:
// Create an uninitialized object
T graph = (T)FormatterServices.GetUninitializedObject(graphType);
// (graph, serializationInfo, streamingContext) => graph.Constructor(serializationInfo, streamingContext)
ParameterExpression graphParameter = Expression.Parameter(serializationPack.SelfSerializingBaseClassType, "graph");
ParameterExpression serializationInfoParameter = Expression.Parameter(typeof(SerializationInfo), "serializationInfo");
ParameterExpression streamingContextParameter = Expression.Parameter(typeof(StreamingContext), "streamingContext");
MethodCallExpression callDeserializationConstructor = Expression.Call(graphParameter,
(MethodInfo)serializationPack.SelfSerializingBaseClassType.GetConstructor(new[] { typeof(SerializationInfo), typeof(StreamingContext) }),
new[] { serializationInfoParameter, streamingContextParameter });
但Expression.Call
只接受MethodInfo
而非ConstructorInfo
,因此无效 - 除非有办法转换为MethodInfo
?
更新
我只是使用ConstructorInfo.Invoke
:
// Cache this part
ConstructorInfo deserializationConstructor = serializationPack
.SelfSerializingBaseClassType
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, CallingConventions.Standard,
new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null);
// Call this when I need it
deserializationConstructor.Invoke(graph, new Object[] { serializationInfo, new StreamingContext() });
我害怕它的表现,但它似乎是唯一的方法。
更新
现在有一个正确的答案。谢谢大家。
答案 0 :(得分:6)
如果我正确地阅读了你的问题,你并不关心是否通过表达式树调用构造函数,只要实际的调用不需要反射。您可以构建一个转发到构造函数调用的动态方法:
using System;
using System.Reflection;
using System.Reflection.Emit;
namespace ConsoleApplication1
{
static class Program
{
static void Main(string[] args)
{
var constructor = typeof(Foo).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
var helperMethod = new DynamicMethod(string.Empty, typeof(void), new[] { typeof(Foo) }, typeof(Foo).Module, true);
var ilGenerator = helperMethod.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, constructor);
ilGenerator.Emit(OpCodes.Ret);
var constructorInvoker = (Action<Foo>)helperMethod.CreateDelegate(typeof(Action<Foo>));
var foo = Foo.Create();
constructorInvoker(foo);
constructorInvoker(foo);
}
}
class Foo
{
int x;
public static Foo Create()
{
return new Foo();
}
private Foo()
{
Console.WriteLine("Constructor Foo() called, GetHashCode() returns {0}, x is {1}", GetHashCode(), x);
x++;
}
}
}
请注意,这表现得像常规方法调用。在打印其值之前未设置x
,因此当您再次调用构造函数时,它不会重置为0
。根据构造函数的作用,这可能是也可能不是问题。
答案 1 :(得分:6)
如果要使用表达式树,请使用Expression.New。这是一个例子
var info = Expression.Parameter(typeof(SerializationInfo), "info");
var context = Expression.Parameter(typeof(StreamingContext), "context");
var callTheCtor = Expression.New(ctorInfo, info, context);
这不适用于现有对象,但由于您的代码显示GetUninitializedObject
,我认为您可以删除该部分并使用Expression.New
创建新对象。
答案 2 :(得分:2)
我创建了一个方法来为构造函数创建(打开)委托。您可以将它与任何(静态或实例)构造函数一起使用,其中包含任意数量的参数和variaty(如ref和out)。如果您的委托返回void,则构造类型的实例应该是第一个参数。如果委托返回要构造的类型,则将创建一个新实例。
static public T CreateDelegate<T>(this ConstructorInfo constructor)
{
// Validate if the constructor is not null.
if (constructor == null)
throw new ArgumentNullException("constructor");
// Validate if T is a delegate.
Type delegateType = typeof(T);
if (!typeof(Delegate).IsAssignableFrom(delegateType))
throw new ArgumentException("Generic argument T must be a delegate.");
// Get alle needed information.
MethodInfo invoke = delegateType.GetMethod("Invoke");
ParameterInfo[] constructorParams = constructor.GetParameters();
ParameterInfo[] delegateParams = invoke.GetParameters();
// What kind of delegate is going to be created (open, creational, static).
bool isOpen = false;
OpCode opCode = OpCodes.Newobj;
int parameterOffset = 0;
if (constructor.IsStatic) // Open delegate.
{
opCode = OpCodes.Call;
if (invoke.ReturnType != typeof(void))
throw new ArgumentException("Delegate to static constructor cannot have a return type.");
if (delegateParams.Length != 0)
throw new ArgumentException("Delegate to static constructor cannot have any parameters.");
}
else if (invoke.ReturnType == typeof(void)) // Open delegate.
{
opCode = OpCodes.Call;
isOpen = true;
parameterOffset = 1;
if ((delegateParams.Length == 0) || (delegateParams[0].ParameterType != constructor.DeclaringType))
throw new ArgumentException("An open delegate must have a first argument of the same type as the type that is being constructed.");
}
else // Creational delegate.
{
if (invoke.ReturnType != constructor.DeclaringType)
throw new ArgumentException("Return type of delegate must be equal to the type that is being constructed.");
}
// Validate the parameters (if any).
if (constructorParams.Length + parameterOffset != delegateParams.Length)
throw new ArgumentException(isOpen
? "The number of parameters of the delegate (the argument for the instance excluded) must be the same as the number of parameters of the constructor."
: "The number of parameters of the delegate must be the same as the number of parameters of the constructor.");
for (int i = 0; i < constructorParams.Length; i++)
{
ParameterInfo constructorParam = constructorParams[i];
ParameterInfo delegateParam = delegateParams[i + parameterOffset];
if (constructorParam.ParameterType != delegateParam.ParameterType)
throw new ArgumentException("Arguments of constructor and delegate do not match.");
}
// Create the dynamic method.
DynamicMethod method = new DynamicMethod(
"",
invoke.ReturnType,
delegateParams.Select(p => p.ParameterType).ToArray(),
constructor.DeclaringType.Module,
true);
// Create the IL.
ILGenerator gen = method.GetILGenerator();
for (int i = 0; i < delegateParams.Length; i++)
gen.Emit(OpCodes.Ldarg, i);
gen.Emit(opCode, constructor);
gen.Emit(OpCodes.Ret);
// Return the delegate :)
return (T)(object)method.CreateDelegate(delegateType);
}
要创建委托,请使用:
public class MyObject
{
public MyObject(int anyValue)
{
...
}
}
Action<MyObject, int> c = typeof(MyObject)
.GetConstructor(new [] { typeof(int) })
.CreateDelegate<Action<MyObject, int>>();
MyObject myObject = new MyObject(1;
c(myObject, 2);
通过添加额外的功能可以缩短所有这些:
static public T CreateConstructorDelegate<T>(this Type type)
{
// Validate if the constructor is not null.
if (type == null)
throw new ArgumentNullException("type");
// Validate if T is a delegate.
Type delegateType = typeof(T);
if (!typeof(Delegate).IsAssignableFrom(delegateType))
throw new ArgumentException("Generic argument T must be a delegate.");
// Validate the delegate return type
MethodInfo invoke = delegateType.GetMethod("Invoke");
int parameterOffset = 0;
BindingFlags binding = BindingFlags.Public | BindingFlags.Instance;
if (invoke.ReturnType == typeof(void))
{
if (invoke.GetParameters().Length == 0)
binding = BindingFlags.NonPublic | BindingFlags.Static; // For static constructors.
else
parameterOffset = 1; // For open delegates.
}
// Validate the signatures
ParameterInfo[] delegateParams = invoke.GetParameters();
ConstructorInfo constructor = type.GetConstructor(binding, null, delegateParams.Skip(parameterOffset).Select(p => p.ParameterType).ToArray(), null);
if (constructor == null)
throw new ArgumentException("Constructor with specified parameters cannot be found.");
return constructor.CreateDelegate<T>();
}
创建委托的调用现在是:
Action<MyObject, int> c = typeof(MyObject)
.CreateConstructorDelegate<Action<MyObject, int>>();
// Call constructor.
MyObject myObject = new MyObject(1);
// Call constructor again on same object.
c(myObject, 2);
警告! 每次调用这些方法时,都会创建一小段代码。如果你为同一个构造函数调用这些函数,请考虑缓存。
答案 3 :(得分:1)
如果已经创建了对象,则无法调用构造函数。您可以看到在BCL中如何使用此方法,标记为internal
的所有方法和调用构造函数的方法都在公共语言运行时本身中实现:
RuntimeConstructorInfo constructor = ObjectManager.GetConstructor(t);
object uninitializedObject = FormatterServices.GetUninitializedObject((Type) this.m_realType);
constructor.SerializationInvoke(uninitializedObject, this.m_savedSerializationInfo, context);
[DebuggerHidden]
[SecuritySafeCritical]
[DebuggerStepThrough]
[MethodImpl(MethodImplOptions.InternalCall)]
internal static void SerializationInvoke(IRuntimeMethodInfo method, object target, SerializationInfo info, ref StreamingContext context);