编辑:我在午夜左右写下了原始答案。我想这表明了一些问题。听起来你最需要的是如何在参数和返回类型仅在运行时已知的情况下构建委托。您可以通过使用object
来解决此问题,但仍可以进行强类型化。最相关的调用是:
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(A), typeof(NewA));
这将创建委托类型
Func<A, NewA>
,用于将
A
转换为
NewA
。如果在编译时并不知道
A
和
NewA
,可以使用
Expression.Lambda
的此重载来创建
Expression<Func<A, NewA>>
。
好问题。我很乐意尝试使用更强的类型来解决问题。
我对此进行了模拟,并且我认为您可以生成类型为Expression<Func<TOld, TNew>>
的表达式,而不是Expression<Func<object, object>>
。在这些转换函数内部,您不需要进行强制转换。但是,我仍然不确定它是否是更好的解决方案。到目前为止,以下是我的想法:
转换/反射/表达式代码的主要内容位于我称之为ConverterDictionary的类中,该类构建这些转换器,然后将其保存在字典中,并以漂亮的类型方式提供它们。我猜您已经解决了问题的反射和表达式构建部分,因此其中95%对您来说可能并不重要。其他所有内容都是100%的示例代码。
请告诉我需要澄清或进一步解释的内容,或者哪些内容可能更有帮助。
class Program
{
static void Main(string[] args)
{
var cv = new ConverterDictionary();
var type2_converter = cv.GetConverter<Type2, Type2_New>();
var converterF = type2_converter.Compile();
var type2 = new Type2 { Field1 = "Hello", Field2 = "World" };
var type2_New = converterF(type2);
}
}
public class ConverterDictionary
{
private Dictionary<Tuple<Type, Type>, object> conversionDict = new Dictionary<Tuple<Type, Type>, object>();
public ConverterDictionary()
{
var convertPairs = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.CustomAttributes.Any(a => a.AttributeType == typeof(ClassConvertAttribute)))
.Select(t => Tuple.Create(t, (Type)(t.CustomAttributes.First().NamedArguments[0].TypedValue.Value)))
.ToList();
foreach(var pair in convertPairs)
{
var fromType = pair.Item1;
var toType = pair.Item2;
var fieldConversions = fromType.GetFields()
.Where(f => f.CustomAttributes.Any(a => a.AttributeType == typeof(FieldConvertAttribute)))
.Select(f => Tuple.Create(f, toType.GetField((string)(f.CustomAttributes.First().NamedArguments[0].TypedValue.Value))))
.ToList();
var delegateType = typeof(Func<,>).MakeGenericType(fromType, toType);
var param1 = Expression.Parameter(fromType, "oldInst");
var returnVar = Expression.Variable(toType, "newInst");
var expr = Expression.Lambda(
delegateType,
Expression.Block(
new ParameterExpression[] { returnVar },
new Expression[]
{
Expression.Assign(
returnVar,
Expression.New(toType)
),
}.Concat(
fieldConversions.Select(fc =>
Expression.Assign(
Expression.MakeMemberAccess(
returnVar,
fc.Item2
),
Expression.MakeMemberAccess(
param1,
fc.Item1
)
)
)
).Concat(
new Expression[] { returnVar }
)
),
param1
);
conversionDict[pair] = expr;
}
}
public Expression<Func<TFrom, TTo>> GetConverter<TFrom, TTo>()
{
var key = Tuple.Create(typeof(TFrom), typeof(TTo));
if (conversionDict.ContainsKey(key))
return conversionDict[key] as Expression<Func<TFrom, TTo>>;
return null;
}
}
[ClassConvert(ToType=typeof(Type1_New))]
public class Type1
{
[FieldConvert(ToFieldName = "Field1")]
public string Field1;
}
[ClassConvert(ToType = typeof(Type2_New))]
public class Type2
{
[FieldConvert(ToFieldName="Field1")]
public string Field1;
[FieldConvert(ToFieldName = "Field2_New")]
public string Field2;
}
public class Type1_New
{
public string Field1;
}
public class Type2_New
{
public string Field1;
public string Field2_New;
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class ClassConvertAttribute : Attribute
{
public Type ToType { get; set; }
}
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class FieldConvertAttribute : Attribute
{
public string ToFieldName { get; set; }
}