通用的字符串泛型类型转换

3
我的任务是编写一个方法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()将是效率最低的)。 - Dave Bish
2
你可以使用以下代码替换 if (typeT.GetInterface("IConvertibleFromString") != null) { return (bool)typeT.GetMethod("FromString").Invoke(value, new object[] { str }); }var iface = value as IConvertibleFromString; if (iface != null) return iface.FromString(str);。这样可以避免反射带来的性能损失。 - corrego
3个回答

5

这对我来说似乎是最好的。我用各种消费者进行了数百万次迭代。这是结合了人们的评论和一些额外内容。

    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;

    }

1
啥?IConvertible.FromString()?你从哪搞来的? - Szybki

1

对于情况#1,它已经是最佳的。

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

对于情况#3,方法FromString要么是静态工厂方法,因此不包含在接口IConvertibleFromString中(因此接口只是类型标记,不包含任何方法),要么是实例方法,用于改变this._value或其他什么,这不清楚。如果是后者,则只需将value强制转换为IConvertibleFromString并调用FromString,无需反射。如果是静态工厂方法,则必须使用反射。


0

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

如果你想让它完美地工作,这是我的建议:

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

  2. 创建一个Type类型的方法 - StringActivator.CreateInstance(string...)

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

  4. 在内部,遍历类型(T)的构造函数,并找到所有具有请求的参数数量的构造函数(如在激活字符串中发送的那样)。

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

为了使其正常工作,您需要在CreateInstance的开头检查T是原始类型还是枚举类型(递归的停止条件),然后使用(T)Convert进行转换。

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

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

希望这可以帮助您。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接