Добросовестное использование или злоупотребление динамическая (система.Динамический.DynamicObject)?


У меня есть проект шаблонов T4, который генерирует методы, чтобы "конвертировать" один тип (класс, структура, перечисление) из исходной сборки соответствующего типа в другой. Оба типа имеют одинаковые имена и имена свойств, просто из разных пространств и разных типов. Это может быть повторно изобрел колесо. Тем не менее, я не нашел тут каких-либо решений там, и она почувствовала, как хорошее упражнение программирования в любом случае. Я также не знаком с динамическими языками, и хотя это целесообразным применение динамического (системы.Динамический.DynamicObject) сайта.

Это следующие .TT-файл, который записывает все "преобразования" методов, но большая часть работы осуществляется в статическом классе с многочисленными вспомогательными методами расширения.

<#@ template language="C#" inherits="CommonT4.BaseTemplate" #>
<#@ Import Namespace="CommonT4" #>
<#@ Import Namespace="System.Collections.Generic" #>
<#@ Import Namespace="System.Text" #>
<#@ Import Namespace="System.Linq" #>
<#@ Import Namespace="System.IO" #>
<#@ Import Namespace="System.Reflection" #>
namespace <#= Namespace #>

{
    public static class ConvertExtension
    {
<#
    //will this work for ValueType (enum, struct)?
    foreach (var typeTuple in this.GetConvertTypeMappings(this.SrcAssembly, this.DestAssembly))
    {
        Type destType = typeTuple.Item2;
        Type srcType = typeTuple.Item1;

#>
        public static <#=destType.ToGenericTypeFullNameString()#> Convert(this <#=srcType.ToGenericTypeFullNameString() #> src0)
        {
            dynamic dest0;
            dynamic <#=HelperExtensionT4.DynamicVariableNameDeclarationsString(1, MAX_LVL) #>

    <#
            HelperExtensionT4.WriteConvertType(this, destType, srcType);
    #>
            return dest0;   
        }   
<#
    }
#>  
    }
}
<#+
    public string Namespace;
    public static int MAX_LVL = 16;
#>

Обратите внимание на первую строку в каждый генерируемый метод Convert объявит серию динамических переменных, большинство из которых будут использованы. Причина, по которой я использовал динамические ключевые слова, чтобы я мог использовать те же имена переменных, даже если эти типы назначены на эти переменные последующих изменениях. Это Т4 функция шаблона "WriteConvertType" легче написать, на мой взгляд. Чтобы уточнить, динамический ключевое слово не используется в любом месте внутри этого вспомогательный метод - динамического сайта будут отображаться в сгенерированном коде со всеми методами преобразования. В конце вопрос я выложу фрагмент, что сгенерированный код выглядит, так как он является длительным. Это вспомогательный класс, в котором определен рекурсивный метод расширения "WriteConvertType" является.

public static void WriteConvertType(BaseTemplate template, Type destType, Type srcType, Stack<Type> stack = null)
        {   
            if (stack == null)
                stack = new Stack<Type>();

            if (srcType.IsNullableType())
            {
                template.WriteLine("dest{0} = new {1}(default({2}));", stack.Count, destType.ToGenericTypeFullNameString(), destType.GetGenericArguments()[0].ToGenericTypeFullNameString());           

                return;
            }
            else
                template.WriteLine("dest{0} = new {1}();", stack.Count, destType.ToGenericTypeFullNameString());            

            var commonProperties = from tuple in template.GetCommonConvertProperties(destType, srcType)
                             select new { desttype = tuple.Item1, srctype = tuple.Item2, Name = tuple.Item3 };
            foreach (var common in commonProperties)
            {
                Type srcElementType;
                Type mappedType;
                if (common.desttype == common.srctype || template.IsTypeMapped(common.desttype, out mappedType))//base case: would-be return;
                    template.WriteLine("dest{0}.{1} = src{2}.{3};", stack.Count, common.Name, stack.Count, common.Name);
                else if (common.srctype.isGenericEnumerableType(out srcElementType))
                {
                    //get matching generic argument/element Type from dest. Assembly or mscorlib:
                    Type destElementType = (from dtype in template.DestAssembly.GetTypes().Union(mscorlib.GetTypes())
                                            where dtype.Name == srcElementType.Name
                                            select dtype).First();
                    template.WriteLine("if (src{0}.{1} != null) {{", stack.Count, common.Name);
                    template.PushIndent("    ");
                    template.WriteLine("dest{0}.{1} = new {2}();"
                        , stack.Count
                        , common.Name
                        , common.desttype.ToGenericTypeFullNameString());                   
                    //caller checks stack:
                    if (!stack.Contains(common.desttype))
                    {
                        template.WriteLine("for (int i{0} = 0; i{0} < src{0}.{1}.Count; i{0}++)"
                           , stack.Count
                           , common.Name);
                        template.WriteLine("{");
                        template.PushIndent("    ");
                        template.WriteLine("src{0} = src{1}.{2}[i{1}];", stack.Count + 1, stack.Count, common.Name);
                        stack.Push(destElementType);
                        WriteConvertType(template, destElementType, srcElementType, stack);//, lvl + 1);
                        stack.Pop();
                        template.WriteLine("dest{0}.{1}.Add(dest{2});", stack.Count, common.Name, stack.Count + 1);
                        template.PopIndent();
                        template.WriteLine("}");//end for
                    }                                           
                    template.PopIndent();
                    template.WriteLine("}");//end if
                }
                else
                {
                    if (!stack.Contains(common.desttype))
                    {
                        template.WriteLine("src{0} = src{1}.{2};", stack.Count + 1, stack.Count, common.Name);//if property type not equal (and not IEnumerable)
                        template.WriteLine("if (src{0} is {1}) {{", stack.Count + 1, common.srctype.ToGenericTypeFullNameString());//if src != null
                        template.PushIndent("    ");//CurrentIndent += 1;
                        stack.Push(common.desttype);
                        WriteConvertType(template, common.desttype, common.srctype, stack);// lvl + 1);
                        stack.Pop();
                        template.WriteLine("dest{0}.{1} = dest{2};", stack.Count, common.Name, stack.Count + 1);//finally, set the property upon return
                        template.PopIndent();
                        template.WriteLine("}");
                    }                   
                }
            }
            return;
        }

Будет ли это считаться чрезмерное использование или не идеальная ситуация для использования в C# динамическое сайта? Это влечет заметное снижение производительности? Существуют ли рекомендации или шаблоны/практики для ЭБР?

Кроме того, "преобразование" хорошее название для метода? Цель этого метода не представляется преобразования операции, хотя это не совсем бросание. Цель будет в конечном итоге маршал исходного типа, которые не сериализуемы по системе WCF datacontractserializer для назначения тип, который будет сериализовать.

Спасибо за чтение. Я выложил исходный код для этого здесь: http://wcfsilverlighthelper.codeplex.com/

Наконец, вот пример одного из созданных методов.

public static Northwind.SL.Model.Product Convert(this Northwind.NET.Model.Product src0)
    {
        dynamic dest0;
        dynamic src1, dest1, src2, dest2, src3, dest3, src4, dest4, src5, dest5, src6, dest6, src7, dest7, src8, dest8, src9, dest9, src10, dest10, src11, dest11, src12, dest12, src13, dest13, src14, dest14, src15, dest15, src16, dest16;

        dest0 = new Northwind.SL.Model.Product();
        dest0.ID = src0.ID;
        src1 = src0.PermissionType;
        if (src1 is System.Nullable<Northwind.NET.Security.PermissionTypeEnum>)
        {
            dest1 = new System.Nullable<Northwind.SL.Security.PermissionTypeEnum>(default(Northwind.SL.Security.PermissionTypeEnum));
            dest0.PermissionType = dest1;
        }
        src1 = src0.SecureName;
        if (src1 is Northwind.NET.Security.SecureString)
        {
            dest1 = new Northwind.SL.Security.SecureString();
            src2 = src1.SecurityHandle;
            if (src2 is Northwind.NET.Security.SecurityHandle)
            {
                dest2 = new Northwind.SL.Security.SecurityHandle();
                dest2.Domain = src2.Domain;
                dest1.SecurityHandle = dest2;
            }
            dest1.Value = src1.Value;
            dest0.SecureName = dest1;
        }
        dest0.Name = src0.Name;
        src1 = src0.SecurityHandle;
        if (src1 is Northwind.NET.Security.SecurityHandle)
        {
            dest1 = new Northwind.SL.Security.SecurityHandle();
            dest1.Domain = src1.Domain;
            dest0.SecurityHandle = dest1;
        }
        dest0.SupplierID = src0.SupplierID;
        dest0.CategoryID = src0.CategoryID;
        dest0.QuantityPerUnit = src0.QuantityPerUnit;
        dest0.UnitPrice = src0.UnitPrice;
        dest0.UnitsInStock = src0.UnitsInStock;
        dest0.UnitsOnOrder = src0.UnitsOnOrder;
        dest0.ReorderLevel = src0.ReorderLevel;
        dest0.Discontinued = src0.Discontinued;
        dest0.RowTimeStamps = src0.RowTimeStamps;
        src1 = src0.Supplier;
        if (src1 is Northwind.NET.Model.Supplier)
        {
            dest1 = new Northwind.SL.Model.Supplier();
            dest1.ID = src1.ID;
            dest1.Name = src1.Name;
            src2 = src1.SecureName;
            if (src2 is Northwind.NET.Security.SecureString)
            {
                dest2 = new Northwind.SL.Security.SecureString();
                src3 = src2.SecurityHandle;
                if (src3 is Northwind.NET.Security.SecurityHandle)
                {
                    dest3 = new Northwind.SL.Security.SecurityHandle();
                    dest3.Domain = src3.Domain;
                    dest2.SecurityHandle = dest3;
                }
                dest2.Value = src2.Value;
                dest1.SecureName = dest2;
            }
            src2 = src1.PermissionType;
            if (src2 is System.Nullable<Northwind.NET.Security.PermissionTypeEnum>)
            {
                dest2 = new System.Nullable<Northwind.SL.Security.PermissionTypeEnum>(default(Northwind.SL.Security.PermissionTypeEnum));
                dest1.PermissionType = dest2;
            }
            dest1.ContactName = src1.ContactName;
            dest1.ContactTitle = src1.ContactTitle;
            dest1.Address = src1.Address;
            dest1.City = src1.City;
            dest1.Region = src1.Region;
            dest1.PostalCode = src1.PostalCode;
            dest1.Country = src1.Country;
            dest1.Phone = src1.Phone;
            dest1.Fax = src1.Fax;
            dest1.HomePage = src1.HomePage;
            dest1.RowTimeStamps = src1.RowTimeStamps;
            if (src1.Products != null)
            {
                dest1.Products = new System.Collections.Generic.List<Northwind.SL.Model.Product>();
                for (int i1 = 0; i1 < src1.Products.Count; i1++)
                {
                    src2 = src1.Products[i1];
                    dest2 = new Northwind.SL.Model.Product();
                    dest2.ID = src2.ID;
                    src3 = src2.PermissionType;
                    if (src3 is System.Nullable<Northwind.NET.Security.PermissionTypeEnum>)
                    {
                        dest3 = new System.Nullable<Northwind.SL.Security.PermissionTypeEnum>(default(Northwind.SL.Security.PermissionTypeEnum));
                        dest2.PermissionType = dest3;
                    }
                    src3 = src2.SecureName;
                    if (src3 is Northwind.NET.Security.SecureString)
                    {
                        dest3 = new Northwind.SL.Security.SecureString();
                        src4 = src3.SecurityHandle;
                        if (src4 is Northwind.NET.Security.SecurityHandle)
                        {
                            dest4 = new Northwind.SL.Security.SecurityHandle();
                            dest4.Domain = src4.Domain;
                            dest3.SecurityHandle = dest4;
                        }
                        dest3.Value = src3.Value;
                        dest2.SecureName = dest3;
                    }
                    dest2.Name = src2.Name;
                    src3 = src2.SecurityHandle;
                    if (src3 is Northwind.NET.Security.SecurityHandle)
                    {
                        dest3 = new Northwind.SL.Security.SecurityHandle();
                        dest3.Domain = src3.Domain;
                        dest2.SecurityHandle = dest3;
                    }
                    dest2.SupplierID = src2.SupplierID;
                    dest2.CategoryID = src2.CategoryID;
                    dest2.QuantityPerUnit = src2.QuantityPerUnit;
                    dest2.UnitPrice = src2.UnitPrice;
                    dest2.UnitsInStock = src2.UnitsInStock;
                    dest2.UnitsOnOrder = src2.UnitsOnOrder;
                    dest2.ReorderLevel = src2.ReorderLevel;
                    dest2.Discontinued = src2.Discontinued;
                    dest2.RowTimeStamps = src2.RowTimeStamps;
                    dest1.Products.Add(dest2);
                }
            }
            dest0.Supplier = dest1;
        }
        return dest0;
    }


1026
3
c#
задан 1 июня 2011 в 09:06 Источник Поделиться
Комментарии
2 ответа

С AutoMapper (automapper.codeplex.com), ваш полный код может быть уплощена вниз:

using SLProduct = Northwind.SL.Model.Product;
using NetProduct = Northwind.NET.Model.Product;

//

SLProduct slProduct = Mapper.Map<NetProduct, SLProduct>(nlProduct);

Как вы сказали, что типа используйте те же типы свойств и имен, вам не понадобится для настройки режима отображения.

8
ответ дан 2 июня 2011 в 05:06 Источник Поделиться

Я не думаю, что это либо "добросовестное использование" и "злоупотребление" динамический; я думаю, для ваших целей, динамика может быть "неправильно".

Если все вы хотите сделать, - это карта "два типа имеют одинаковые имена и имена свойств, просто из разных пространств и разных видах", то вы, вероятно, следует использовать класс Mapper с метод функция, которая сопоставляет типы.

public static class ObjectMapper
{
private static readonly Dictionary<Tuple<Type, Type>, object> Maps
= new Dictionary<Tuple<Type, Type>, object>();

/// <summary>
/// <para>Add a new map</para>
/// </summary>
/// <typeparam name="TFrom">From Type</typeparam>
/// <typeparam name="TTo">To Type</typeparam>
/// <param name="map">Mapping delegate</param>
public static void AddMap<TFrom, TTo>(Action<TFrom, TTo> map)
where TFrom : class
where TTo : class
{
Maps.Add(Tuple.Create(typeof(TFrom), typeof(TTo)), map);
}

/// <summary>
/// <para>Map object data to another object</para>
/// </summary>
/// <typeparam name="TFrom">From type</typeparam>
/// <typeparam name="TTo">To type</typeparam>
/// <param name="from">From object</param>
/// <param name="to">To object</param>
public static void Map<TFrom, TTo>(TFrom from, TTo to)
{
var key = Tuple.Create(typeof(TFrom), typeof(TTo));
var message = string.Format("No map defined for {0} => {1}", typeof(TFrom).Name, typeof(TTo).Name);

if (!Maps.ContainsKey(key))
throw new Exception(message);

var map = (Action<TFrom, TTo>)Maps[key];

if (map == null)
throw new Exception(message);

map(from, to);
}
}

Кроме того, если вы хотите сделать что-то действительно общими, чтобы сопоставить TInput экземпляр с Тутпач экземпляр без необходимости написания специальной функции отображения для них, вы можете использовать отражение, чтобы создать метод расширения объекта называется CastTo вот так:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

public static class TypeHelper
{
public static TDestType CastTo<TDestType>(this object myType) where TDestType : new()
{
List<MemberInfo> tOrigMembers = myType.GetType().FindMembers(MemberTypes.Property | MemberTypes.Field,
BindingFlags.GetProperty |
BindingFlags.SetProperty |
BindingFlags.SetField |
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance,
null, null).ToList();
List<MemberInfo> tDestMembers = typeof (TDestType).FindMembers(MemberTypes.Property | MemberTypes.Field,
BindingFlags.SetProperty |
BindingFlags.SetField |
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance,
null, null).ToList();
var destTypeInstance = new TDestType();
foreach (MemberInfo m in tOrigMembers)
{
object value = (m is PropertyInfo)
? ((PropertyInfo) m).GetValue(myType, null)
: ((FieldInfo) m).GetValue(myType);
object enumValue;
MemberInfo thisMember = FindMemberMatch(m.Name, value, tDestMembers, out enumValue);
if (thisMember == null)
continue;

if (thisMember is PropertyInfo)
{
var pInfo = (PropertyInfo) thisMember;
pInfo.SetValue(destTypeInstance, pInfo.PropertyType.IsEnum ? enumValue : value, null);
continue;
}
if (thisMember is FieldInfo)
{
var fInfo = (FieldInfo) thisMember;
fInfo.SetValue(destTypeInstance, fInfo.FieldType.IsEnum ? enumValue : value);
continue;
}
}
return destTypeInstance;
}

private static MemberInfo FindMemberMatch(string name, object value, IEnumerable<MemberInfo> members,
out object enumValue)
{
enumValue = null;
Type valueType = value == null ? typeof (object) : value.GetType();
foreach (MemberInfo memberInfo in members)
{
if (memberInfo.Name != name)
continue;
if (memberInfo is FieldInfo)
{
var fieldInfo = (FieldInfo) memberInfo;
if (fieldInfo.FieldType == valueType && fieldInfo.FieldType.IsAssignableFrom(valueType))
return fieldInfo;
if (fieldInfo.FieldType.IsEnum && value != null &&
EnumIsAssignableFromValue(fieldInfo.FieldType, value))
{
enumValue = Enum.Parse(fieldInfo.FieldType, value.ToString(), true);
return fieldInfo;
}
continue;
}
if (memberInfo is PropertyInfo)
{
var propertyInfo = (PropertyInfo) memberInfo;
if (propertyInfo.PropertyType == valueType && propertyInfo.PropertyType.IsAssignableFrom(valueType))
return propertyInfo;
if (propertyInfo.PropertyType.IsEnum && value != null &&
EnumIsAssignableFromValue(propertyInfo.PropertyType, value))
{
enumValue = Enum.Parse(propertyInfo.PropertyType, value.ToString(), true);
return propertyInfo;
}
continue;
}
}
return null;
}

private static bool EnumIsAssignableFromValue(Type enumType, object value)
{
return Enum.GetNames(enumType).Contains(value.ToString()) ||
Enum.GetValues(enumType).OfType<object>().Contains(value);
}
}

Чтобы использовать его, вы могли бы сделать

var myObjectOfTypeA = myObjectOfTypeB.CastTo<TypeA>();

1
ответ дан 2 июня 2011 в 06:06 Источник Поделиться