【问题标题】:Universal generic type conversion from string字符串的通用泛型类型转换
【发布时间】:2013-05-15 06:08:40
【问题描述】:

我的任务是编写一个方法StringToType(),将字符串转换为指定的类型T。

  1. 对于原始类型,我使用 Convert.ChangeType() 方法
  2. 对于枚举类型 - Enum.TryParse()
  3. 对于所有其他自定义类型,我创建了一个接口“IConvertibleFromString”,其中包含一个方法“FromString()”以将字符串转换为指定类型。任何需要从字符串转换的类都必须实现这个接口。

但我不喜欢我实现方法 StringToType() 的方式。我想使用比反射少的多,尽可能保证性能。

请告知如何最好地实施/更改它。

class Program
{
    static bool StringToType<T>(string str, ref T value)
    {
        Type typeT = typeof(T);
        bool isSuccess = false;
        if (typeT.GetInterface("IConvertibleFromString") != null)
        {
            return (bool)typeT.GetMethod("FromString").Invoke(value, new object[] { str });
        }
        else if (typeT.IsEnum)
        {
            MethodInfo methodTryParse = typeT.GetMethod("TryParse").MakeGenericMethod(typeT);
            return (bool)methodTryParse.Invoke(null, new object[] { str, value });
        }
        else if (typeT.IsPrimitive)
        {
            value = (T)Convert.ChangeType(str, typeT);
            return true;
        }
        return isSuccess;
    }

    static void Main(string[] args)
    {
        string intStr = "23";
        int val1 = 0;
        bool res = StringToType<int>(intStr, ref val1);
        Class1 c1;
        res = StringToType<Class1>(intStr, ref c1);
        Console.ReadKey();
    }
}

interface IConvertibleFromString
{
    bool FromString(string str);
}

class MySomeClass : IConvertibleFromString
{
    int someVal;

    public bool FromString(string str)
    {
        return int.TryParse(str, out someVal);
    }
}

【问题讨论】:

  • 一个简单的优化是以最便宜的顺序检查布尔条件(假设 GetInterface() 效率最低)
  • 您可以使用var iface = value as IConvertibleFromString; if (iface != null) return iface.FromString(str); 而不是if (typeT.GetInterface("IConvertibleFromString") != null) { return (bool)typeT.GetMethod("FromString").Invoke(value, new object[] { str }); }。这将为您节省反射惩罚

标签: c# generics type-conversion


【解决方案1】:

这似乎对我来说表现最好。与各种消费者一起进行了一百万次迭代。它是人们的 cmets 的组合,还有一点额外的。

    static Boolean TryParseString<T>(
        String stringValue, ref T value)
    {

        Type typeT = typeof(T);

        if (typeT.IsPrimitive)
        {
            value = (T)Convert.ChangeType(stringValue, typeT);
            return true;
        }
        else if (typeT.IsEnum)
        {
            value = (T)System.Enum.Parse(typeT, stringValue); // Yeah, we're making an assumption
            return true;
        }
        else
        {
            var convertible = value as IConvertible;
            if (convertible != null)
            {
                return convertible.FromString(stringValue);
            }
        }

        return false;

    }

【讨论】:

  • 嗯? IConvertible.FromString()?你从哪里得到的?
【解决方案2】:

对于案例#1,它已经是最优的了。

对于案例 #2,您可以使用 Enum.Parse 并捕获 ArgumentException 并返回 false。

对于案例 #3 方法 FromString 要么是静态工厂方法,因此不包含在接口 IConvertibleFromString 中(因此该接口只是一个类型标记,不包含任何方法),或者它是一个发生变异的实例方法this._value 什么的,不清楚。如果是后者,则只需将value 转换为IConvertibleFromString 并调用FromString,无需反思。如果是静态工厂方法,则必须使用反射。

【讨论】:

    【解决方案3】:

    当前的事物结构不允许它成为真正的 StringToType,因为您不能在拥有“已激活”实例之前调用 FromString。当您的 FromString 被调用时,会创建并返回另一个实例 - 这意味着您每次都需要创建 2 个实例来实现您想要的。

    如果您希望它完美运行,这是我的建议:

    1. 创建一个新类 - “StringActivator”(灵感来自 Reflection 的 Activator)

    2. 创建类型的方法——StringActivator.CreateInstance(string...)

    3. 激活字符串是一个由逗号分隔/xml 字符串组成的字符串,其中每个项目都是构造函数参数。

    4. 在里面,检查类型 (T) 构造函数并找到所有具有请求数量的参数(在激活字符串中发送)的构造函数。

    5. 现在,一旦您拥有所有构造函数,请尝试使用递归将您在激活字符串中发送的每个参数值转换为构造函数期望的类型(调用 StringActivator.CreateInstane,其中 T 是构造函数期望的类型) .

    为此,您需要检查 CreateInstance 的开头是否 T 是 Primitive 或 ENum(递归的停止条件),然后为此使用 (T)Convert。

    1. 当您转换所有以逗号分隔的参数时,请使用 Reflection.Emit 为您发送的特定数量的参数创建编译调用。

    2. 将发出的函数的委托映射到 Type sent () 并在下次尝试从具有相同数量参数的字符串转换为 T 时调用它...

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 2010-09-05
      • 2013-09-30
      • 1970-01-01
      • 2011-06-29
      • 2021-10-25
      • 2019-09-10
      • 1970-01-01
      • 1970-01-01
      • 2014-06-30
      相关资源
      最近更新 更多