【问题标题】:Generic ToType() extension - is there a better way?通用 ToType() 扩展 - 有更好的方法吗?
【发布时间】:2013-11-15 02:28:31
【问题描述】:

以下工作,测试通过。但我对必须在调用中显式传递程序集名称感到不满。有没有更好的办法?

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&lt;T&gt;();,我会收到以下错误:

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

更新2

获奖代码(包括相关的列表扩展)是,感谢,如下。顺便说一句,这些扩展适用于匿名类型,例如由 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;
    }

【问题讨论】:

  • 你能解释一下你在做什么吗?
  • 它是一个轻量级对象到对象映射器作为通用对象扩展 - 给定对象 A,A 的属性名称到 B 的属性名称的映射(字典)返回 B 类型的填充实例。
  • 也许我遗漏了一些非常明显的东西,但是您有什么理由不使用 Activator.CreateInstance&lt;T&gt;() 泛型重载来代替?你可以在那个时候拥有T tmp = Activator.CreateInstance&lt;T&gt;();,它将是类型安全的没有强制转换,你的调用代码将是Target row = item.ToType&lt;Target&gt;(Target.map)?编辑:或者如果你喜欢(这可能会更好,因为它 已经 通用并且需要无参数构造函数)是添加 new 约束。然后它就变成了T tmp = new T();

标签: c# generics reflection extension-methods


【解决方案1】:

是的,您已经拥有所需的代码。只需将其移至通用函数即可。

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

【讨论】:

  • 除非它没有返回正确的程序集 - 请参阅更新了解详情。
【解决方案2】:

我会使用Activator.CreateInstance&lt;T&gt;() 泛型重载并跳过汇编位。这也会强输入它:

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

【讨论】:

  • 这是我获得有效 Activator.CreateInstance 的唯一方法 --- 可能是因为类型声明和扩展存在于不同的程序集中(在我的解决方案中)?我喜欢新约束带来的改进……我知道必须有更好的方法……
  • 关于扩展方法在不同的程序集中,我认为这无关紧要。当您调用它时,您已经在输入已解析的类型。使用这个重载仍然不适合你吗?它会抛出什么异常?
  • 你说的有道理,但没用。我添加了异常详细信息作为问题的更新。
  • 选择了您的答案。我想了解在没有新约束的情况下会发生什么——不足以提出另一个问题——如果对你有意义,也许在这里回复?
  • @marfarma:从错误消息来看,您似乎传递了 abstract 类型的 T 类。但是您发布的代码包含任何abstract 类;您是否尝试使用其他类或您发布的代码不完整?
【解决方案3】:

也许类似的东西更好 - 它大大简化了您的通话:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 2023-01-31
    • 1970-01-01
    相关资源
    最近更新 更多