2013-11-15 3 views
0

Следующие работы, тест проходит. Но я недоволен необходимостью явного пропуска имени сборки в вызове. Есть ли способ лучше?Универсальное расширение ToType() - есть ли лучший способ?

public static object ToType<T>(this object obj, T type, string assembly, IDictionary<string,string> maps) { 
    //create instance of T type object: 
    var tmp = Activator.CreateInstance(assembly, type.ToString()); 

    foreach(var map in maps) { 
     try 
     { 
      PropertyInfo source = obj.GetType() 
       .GetProperty(map.Value); 

      tmp.Unwrap().GetType().GetProperty(map.Key) 
       .SetValue(tmp.Unwrap(), source.GetValue(obj, null), null); 
     } 
     catch 
     { 
      throw new ArgumentException("Error converting to Type: "+type); 
     } 
    } 
    return tmp.Unwrap(); 
} 

[Test] 
public void TestToTypeExtension() 
{ 
    Source item = new Source(); 
    item.OtherObj_One  = "1234567890"; 
    item.OtherObj_Code  = "IBM.N"; 
    item.OtherObj_CodeType = "S"; 
    item.OtherObj_CodeGroup = "EQUITY"; 

    Target row = (Target)item.ToType(typeof(Target), ((typeof(Target)).Assembly).FullName, Target.map); 

    Assert.AreEqual(item.OtherObj_One, row.One); 
    Assert.AreEqual(item.OtherObj_Code, row.Code); 
    Assert.AreEqual(item.OtherObj_CodeType, row.CodeType); 
} 

public class Target 
{ 
    public static Dictionary<String, String> map = new Dictionary<string, string>{ 
                {"One"   ,"OtherObj_One"}, 
                {"Code"   ,"OtherObj_Code"}, 
                {"CodeType"  ,"OtherObj_CodeType"}, 

    }; 

    public String One { get; set; } 
    public String Code { get; set; } 
    public String CodeType { get; set; } 

} 

public class Source 
{ 
    public String OtherObj_One { get; set; } 
    public String OtherObj_Code { get; set; } 
    public String OtherObj_CodeType { get; set; } 
    public String OtherObj_CodeGroup { get; set; } 

} 

Update:

Значение ((typeof(T)).Assembly).FullName выполняется внутри метода расширения является: mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Если я изменяю заявление создания объекта T tmp = Activator.CreateInstance<T>(); я получаю следующее сообщение об ошибке:

Test Name: TestToTypeExtension 
Test FullName: Solution.Test.UtilsTests.TestToTypeExtension 
Test Source: [ ... ]\UnitTestProject1\UtilsTests.cs : line 39 
Test Outcome: Failed 
Test Duration: 0:00:00.071 

Result Message: System.MissingMethodException : Cannot create an abstract class. 
Result StackTrace: 
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) 
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) 
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark) 
at System.Activator.CreateInstance[T]() 
at Util.MappingExtensions.ToType[T](Object obj, T type, String assembly, IDictionary`2 maps) in [ ... ]\Utils\MappingExtensions.cs:line 22 
at Solution.Test.UtilsTests.TestToTypeExtension() in [ ... ]\UnitTestProject1\UtilsTests.cs:line 46 

Update2

Код выигрыша (включая связанный с ним список) является следующим: Кстати, расширения работают для анонимных типов, таких как те, которые были возвращены выражениями linq select new (т. Е. select new {prop1 = x, prop2 = y}) - это была моя основная мотивация.

public static object ToType<T>(this object obj, IDictionary<string, string> maps) 
      where T : new() 
    { 
     T tmp = new T(); 
     Type objType = obj.GetType(); 
     Type tType = typeof(T); 

     foreach(var map in maps) { 
      try 
      { 
       PropertyInfo source = objType.GetProperty(map.Value); 

       tType.GetProperty(map.Key) 
         .SetValue(tmp, source.GetValue(obj, null), null); 
      } 
      catch 
      { 
       throw new ArgumentException("Error converting to Type: "+ tType); 
      } 
     } 
     return tmp; 
    } 

    public static List<T> ToTypeList<T,U>(this List<U> source 
        , IDictionary<string, string> maps) 
     where T : new() 
    { 
     List<T> result = new List<T>(); 

     foreach (var item in source) 
     { 
      result.Add((T)item.ToType<T>(maps)); 
     } 
     return result; 
    } 
+1

Можете ли вы объяснить, что это вы делаете? –

+0

Это легкий объект для объекта mapper в качестве расширения общего объекта - с учетом объекта A и карты имен свойств A в именах свойств B (Словарь) возвращает заполненный экземпляр типа B. –

+0

Возможно, я что-то пропустил очевидно, но по какой-либо причине вы не используете [Activator.CreateInstance () '] (http://msdn.microsoft.com/en-us/library/0hcyx2kd%28v=vs.110%29.aspx) вместо общей перегрузки? Вы можете просто иметь в этой точке 'T tmp = Activator.CreateInstance ();', это было бы безопасным по типу _without_ casting, а ваш код вызова был бы 'Target row = item.ToType (Target.map) '? EDIT: Или, если вам нравится (что может быть лучше, так как это _already_ generic и требует конструктора без параметров) заключается в добавлении ограничения 'new'. Тогда он просто становится «T tmp = new T();« –

ответ

1

Я хотел бы использовать Activator.CreateInstance<T>() общую перегрузку и пропустить немного сборки. Это также сильно ввести его:

public static object ToType<T>(this object obj, IDictionary<string,string> maps) { 
    //create instance of T type object: 
    T tmp = Activator.CreateInstance<T>(); 

    foreach(var map in maps) { 
     try 
     { 
      PropertyInfo source = obj.GetType() 
       .GetProperty(map.Value); 

      tmp.Unwrap().GetType().GetProperty(map.Key) 
       .SetValue(tmp.Unwrap(), source.GetValue(obj, null), null); 
     } 
     catch 
     { 
      throw new ArgumentException("Error converting to Type: "+ typeof(T)); 
     } 
    } 

    return tmp.Unwrap(); 
} 

Кроме того, так как вы уже требуете типов иметь конструктор без параметров (с помощью вашего использования Activator.CreateInstance без аргументов параметров), рассмотреть возможность использования new constraint для обеспечения во время компиляции безопасность:

public static object ToType<T>(this object obj, IDictionary<string,string> maps) where T : new() 
{ 
    //create instance of T type object: 
    T tmp = new T(); 

    foreach(var map in maps) { 
     try 
     { 
      PropertyInfo source = obj.GetType() 
       .GetProperty(map.Value); 

      tmp.Unwrap().GetType().GetProperty(map.Key) 
       .SetValue(tmp.Unwrap(), source.GetValue(obj, null), null); 
     } 
     catch 
     { 
      throw new ArgumentException("Error converting to Type: "+ typeof(T)); 
     } 
    } 

    return tmp.Unwrap(); 
} 

Ваше использование может выглядеть следующим образом без необходимости приведения:

Target row = item.ToType<Target>(Target.map) 

Я также не уверен, что происходит с Unwrap() вызовов и вызов .GetType() на них, но я подозреваю, что они излишни здесь, так что, возможно, ваш метод может быть упрощена:

public static object ToType<T>(this object obj, IDictionary<string,string> maps) where T : new() 
{ 
    //create instance of T type object: 
    T tmp = new T(); 

    Type objType = obj.GetType(); 
    Type tType = typeof(T); 

    foreach(var map in maps) { 
     try 
     { 
      PropertyInfo source = objType.GetProperty(map.Value); 

      tType.GetProperty(map.Key) 
       .SetValue(tmp, source.GetValue(obj, null), null); 
     } 
     catch 
     { 
      throw new ArgumentException("Error converting to Type: "+ tType); 
     } 
    } 

    return tmp; 
} 
+0

Это единственный способ получить рабочий Activator.CreateInstance --- возможно, потому что объявление типа и расширение живут в разных сборках (в моем решении)? Мне нравится улучшение, которое создает новое ограничение ... Я знал, что должен быть лучший способ ... –

+0

Что касается метода расширения, находящегося в другой сборке, я не думаю, что это должно иметь значение. Когда вы это называете, вы уже загружаете разрешенный тип. Использование этой перегрузки по-прежнему не работает для вас? Какое это исключение? –

+0

То, что вы здесь говорите, имеет смысл, но оно не работает. Я добавил данные об исключениях в качестве обновления по этому вопросу. –

2

Да, у вас уже есть код, который вам нужен. Просто переместите его в функцию Generic.

var assembly = ((typeof(T)).Assembly).FullName; 
+0

вы избили меня до него –

+0

За исключением того, что он не возвращает правильную сборку - см. Обновление для деталей. –

1

Может быть что-то вроде лучше - это массивно упрощает вызов:

public static T ToType<T>(this object obj, IDictionary<string, string> maps) 
{ 
    //create instance of T type object: 
    var tmp = Activator.CreateInstance(typeof(T).Assembly.FullName, typeof(T).ToString()); 

    foreach (var map in maps) 
    { 
     try 
     { 
      PropertyInfo source = obj.GetType() 
       .GetProperty(map.Value); 

      tmp.Unwrap().GetType().GetProperty(map.Key) 
       .SetValue(tmp.Unwrap(), source.GetValue(obj, null), null); 
     } 
     catch 
     { 
      throw new ArgumentException("Error converting to Type: " + typeof(T)); 
     } 
    } 
    return (T)tmp.Unwrap(); 
} 

Использование:

Target row = item.ToType<Target>(Target.map); 
Смежные вопросы