【问题标题】:How to dynamically parse and compare string values in C#?如何在 C# 中动态解析和比较字符串值?
【发布时间】:2013-10-24 22:08:05
【问题描述】:

抱歉,我不得不重新编辑这个问题。

我需要将两个字符串值动态解析为合适的类型并进行比较,然后返回一个bool结果。

示例 1:

string lhs = “10”;
string rhs = “10”;

Compare.DoesEqual(lhs, rhs, typeof(int)); //true
Compare.DoesEqual(lhs, rhs, typeof(string)); //true

示例 2:

string lhs = “2.0”;
string rhs = “3.1”;

Compare.IsGreaterThan(lhs, rhs, typeof(int)); //false
Compare.IsGreaterThan(lhs, rhs, typeof(double)); //false
Compare.IsGreaterThan(lhs, rhs, typeof(string)); //invalid, always false

目前我正在这样做(我认为这样做很愚蠢):

public partial class Comparer
{
    public static bool DoesEqual(string lhs, string rhs, Type type)
    {
        if (type.Equals(typeof(int)))
        {
            try
            {
                return int.Parse(lhs) > int.Parse(rhs);
            }
            catch
            {
                return false;
            }
        }

        if (type.Equals(typeof(double)))
        {
            try
            {
                return double.Parse(lhs) > double.Parse(rhs);
            }
            catch
            {
                return false;
            }
        }

        return false;
    }
}

还有这个:

public partial class Comparer
{
    public static bool IsGreaterThan(string lhs, string rhs, Type type)
    {
        if (type.Equals(typeof(int)))
        {
            try
            {
                return int.Parse(lhs) == int.Parse(rhs);
            }
            catch
            {
                return false;
            }
        }

        if (type.Equals(typeof(double)))
        {
            try
            {
                return double.Parse(lhs) == double.Parse(rhs);
            }
            catch
            {
                return false;
            }
        }

        if (type.Equals(typeof(string)))
        {
            return lhs.Equals(rhs);
        }

        return false;
    }
}

我正在寻找更好的更好(更通用的方式)实现(也许使用表达式树?)。我很感激任何建议。谢谢!

【问题讨论】:

  • Thyis 不清楚:Compare.IsGreaterThan(lhs, rhs, typeof(string)); //invalid, always false。为什么总是假的?一个字符串总是可以转换成字符串,两个字符串可以比较(字母排序,所以"2.0"

标签: c# parsing compare dynamic


【解决方案1】:

最通用的方法是使用TypeConverter。假设我们有:

string rhs = ...;
string lhs = ...;
Type type = ...;

来自某个地方。你可以这样做:

TypeConverter conv = TypeDescriptor.GetConverter(type);
try
{
    object rho = conv.ConvertFrom(rhs);
    object lho = conv.ConvertFrom(lhs);
    ...
}
catch (NotSupportedException)
{
   // No luck - couldn't parse the string
}

不幸的是,在处理转换器时无法避免catch。现在您已经从字符串中解析了两个对象,您可以进行一般比较。平等很简单——只需使用Object.Equals(最好是静态的——它处理空值):

if (Object.Equals(rho, lho))
{
    ...
}

排序比较,可以查看类型是否支持IComparable

IComparable rhc = rho as IComparable;
if (rhc != null && rhc.CompareTo(lho) < 0) // rhs less than lhs
{ 
    ...
}

但是请注意,某些不提供 operator&lt; 的类型仍然是有序的,因此实现了 IComparable - 例如,String 就是这样做的。

【讨论】:

    【解决方案2】:

    哦,我快要解决这个问题了。 :)

    编辑:测试:

    [TestMethod]
    public void TestDynamicComparer()
    {
        string lhs = "10";
        string rhs = "10";
        Assert.AreEqual(0, DynamicComparer<int>.Default.Compare(lhs, rhs));
    
        lhs = "2.0";
        rhs = "3.1";
        // basic equality
        Assert.AreNotEqual(0, DynamicComparer<double>.Default.Compare(lhs, rhs));
        // correct order
        Assert.IsTrue(DynamicComparer<double>.Default.Compare(lhs, rhs) < 0);
        // check two invalid casts are unordered
        Assert.AreEqual(0, DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs));
    
        // real proof it works
        lhs = "9";
        rhs = "09";
        Assert.AreEqual(0, DynamicComparer<int>.Default.Compare(lhs, rhs));
    
        lhs = "9.0";
        rhs = "09";
        Assert.AreEqual(0, DynamicComparer<double>.Default.Compare(lhs, rhs));
        // test the valid cast is ordered ahead of ("less than") the invalid cast
        Assert.AreNotEqual(0, DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs));
        Assert.IsTrue(DynamicComparer<int>.DefaultNoThrow.Compare(lhs, rhs) > 0);
    }
    
    [TestMethod]
    [ExpectedException(typeof(InvalidCastException))]
    public void TestDynamicComparerInvalidCast()
    {
        // make sure the default comparer throws an InvalidCastException if a cast fails
        string lhs = "2.0";
        string rhs = "3.1";
        DynamicComparer<int>.Default.Compare(lhs, rhs);
    }
    

    胆量:

    public class DynamicComparer<T>
        : IComparer<string>
        , IEqualityComparer<string>
    {
        private static readonly DynamicComparer<T> defaultComparer = new DynamicComparer<T>();
        private static readonly DynamicComparer<T> defaultNoThrowComparer = new DynamicComparer<T>(false);
    
        private DynamicComparerHelper.TryParseDelegate<T> parser = DynamicComparerHelper.GetParser<T>();
        private IComparer<T> comparer;
        private bool throwOnError;
    
        public DynamicComparer()
            : this(Comparer<T>.Default, true)
        {
        }
    
        public DynamicComparer(bool throwOnError)
            : this(Comparer<T>.Default, throwOnError)
        {
        }
    
        public DynamicComparer(IComparer<T> comparer)
            : this(comparer, true)
        {
        }
    
        public DynamicComparer(IComparer<T> comparer, bool throwOnError)
        {
            this.comparer = comparer;
            this.throwOnError = throwOnError;
        }
    
        public static DynamicComparer<T> Default
        {
            get
            {
                return defaultComparer;
            }
        }
    
        public static DynamicComparer<T> DefaultNoThrow
        {
            get
            {
                return defaultNoThrowComparer;
            }
        }
    
        public int Compare(string x, string y)
        {
            T valueX;
            T valueY;
    
            bool convertedX = this.parser(x, out valueX);
            bool convertedY = this.parser(y, out valueY);
    
            if (this.throwOnError && !(convertedX && convertedY))
                throw new InvalidCastException();
    
            if (!(convertedX || convertedY))
                return 0;
    
            if (!convertedX)
                return 1;
    
            if (!convertedY)
                return -1;
    
            return this.comparer.Compare(valueX, valueY);
        }
    
        public bool Equals(string x, string y)
        {
            return Compare(x, y) == 0;
        }
    
        public int GetHashCode(string x)
        {
            T value;
            bool converted = this.parser(x, out value);
    
            if (this.throwOnError && !converted)
                throw new InvalidCastException();
    
            if (!converted)
                return 0;
    
            return value.GetHashCode();
        }
    }
    
    internal class DynamicComparerHelper
    {
        public delegate bool TryParseDelegate<T>(string text, out T value);
    
        private static readonly Dictionary<Type, Delegate> converters =
            new Dictionary<Type, Delegate>()
            {
                { typeof(bool), WrapDelegate<bool>(bool.TryParse) },
                { typeof(short), WrapDelegate<short>(short.TryParse) },
                { typeof(int), WrapDelegate<int>(int.TryParse) },
                { typeof(long), WrapDelegate<long>(long.TryParse) },
                { typeof(ushort), WrapDelegate<ushort>(ushort.TryParse) },
                { typeof(uint), WrapDelegate<uint>(uint.TryParse) },
                { typeof(ulong), WrapDelegate<ulong>(ulong.TryParse) },
                { typeof(float), WrapDelegate<float>(float.TryParse) },
                { typeof(double), WrapDelegate<double>(double.TryParse) },
                { typeof(DateTime), WrapDelegate<DateTime>(DateTime.TryParse) },
            };
    
        public static TryParseDelegate<T> GetParser<T>()
        {
            return (TryParseDelegate<T>)converters[typeof(T)];
        }
    
        private static TryParseDelegate<T> WrapDelegate<T>(TryParseDelegate<T> parser)
        {
            return new TryParseDelegate<T>(parser);
        }
    }
    

    【讨论】:

    • 谢谢。虽然您的解决方案更通用,但在我的具体情况下,stackoverflow.com/questions/1258181/… 更合适。但我相信我会在不久的将来使用您的解决方案。再次感谢您。
    【解决方案3】:

    这是作业吗?

    我的意思是,答案是只为类/方法编写实现。

    您遇到的具体问题是什么?你到底有什么不明白的?

    如果你把问题分解成几个部分,它就变得容易了。

    【讨论】:

    • 这不是家庭作业,我正在寻找一种更通用的方法来做到这一点,而不是单独实现每一件事。我可以看看这个yoda.arachsys.com/csharp/miscutil 有一个通用运算符类,但它不适用于对象类型。
    • 我认为问题可能是“如何编写一个通用转换器,将字符串值动态转换为给定类型并返回正确的类型而不进行强制转换。”否则,正如您所说,我必须手动编写每个单独的实现。
    • 好吧,你不会有'operator'作为一个字符串,你应该把它作为一个类,也许扩展一些基类和一个名为'Operate'的方法。但由于它可以“操作”的各种方式,它必须将“params object [] foo”作为输入。真的有点奇怪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    相关资源
    最近更新 更多