如何在C#中动态解析和比较字符串值?

时间:2009-08-11 02:17:26

标签: c# parsing compare dynamic

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

我需要动态地将两个字符串值解析为适当的类型并进行比较,然后返回一个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;
    }
}

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

3 个答案:

答案 0 :(得分:3)

最通用的方法是使用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<的类型仍然是有序的,因此实施IComparable - 例如,String会这样做。

答案 1 :(得分:1)

哦,我很想拥有这个问题。 :)

编辑:测试:

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

答案 2 :(得分:-2)

这是家庭作业吗?

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

您遇到的具体问题是什么?究竟是什么让你不明白?

如果你将问题分解成部分就变得容易了。