0

以下は機能し、テストはパスします。しかし、呼び出しでアセンブリ名を明示的に渡さなければならないことに不満があります。より良い方法はありますか?

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; }

}

アップデート:

((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

勝利コード (関連する List 拡張を含む) は、次のとおりです。ところで、拡張機能は、linqselect 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;
    }
4

3 に答える 3

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;
}
于 2013-11-15T03:31:14.313 に答える