如何存储类型A和运行时的Func<A,B>类型转换之间的映射?

3

将类型A的映射对象运行时转换为类似类型A'的方式

  1. 使用System.Reflection在运行时发现类型列表。

  2. 对于每个适当的Type,制作一个Expression<Func<object, object>>表达式。该函数将oldType类型的对象转换为newType类型的对象。


附注:

Func<object, object>的泛型类型参数有点误导人,它感觉应该是Func<newType,oldType>,但是这些类型在编译时未知。

Expression<Func<object, object>>  GetConvertExpression(Type oldType, Type newType)

  1. 表达式被编译并存储到一个 Dictionary<Type, Func<object, object>> 中。

问题 - 如何解决当前方法的缺点?

  1. 需要进行字典查找以转换对象。我考虑将一个大的 if/then/elsecase 语句“烘焙”到一个大的“统一”的 Expression 中,这对于少量类型来说可能效果很好。
  2. 在生成的 IL 中存在不必要的强制类型转换操作。
  3. 生成代码的参数和返回类型为 object 类型。这基本上确保了周围将有各种类型的强制类型转换操作。特别是当需要执行 List<oldType>List<newType> 的大规模转换时。

Expression 生成的代码:

object convert(object oldInst)
{
    newType newInst = newType();
    oldType oldInstCast = (oldType)oldInst;
    newInst.field1 = oldInstCast.field1;
    .......
    // note: if some field of oldInst has a type in the DICTIONARY and
    //       it's value is not null, we have to perform the convert
    //       operation recursively for that field
    if(oldInstCast.field3 != null)
    {
        // Type of the field was determined when this code was generated
        // And it is known that the dictionary will contain it
        Func<object, object> transformFun = dictionary[oldFieldType];
        newInst.field3 = (newFieldType)transformFun(oldInstCast.field3);
    }

    newInst.fieldz = oldInstCast.fieldz;

    return newInst;
}
1个回答

1

编辑:我在午夜左右写下了原始答案。我想这表明了一些问题。听起来你最需要的是如何在参数和返回类型仅在运行时已知的情况下构建委托。您可以通过使用object来解决此问题,但仍可以进行强类型化。最相关的调用是:

Type delegateType = typeof(Func<,>).MakeGenericType(typeof(A), typeof(NewA));

这将创建委托类型Func<A, NewA>,用于将A转换为NewA。如果在编译时并不知道ANewA,可以使用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; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接