1

この質問を再編集しなければならなかったことをお詫びします。

2つの文字列値を適切なタイプに動的に解析して比較し、ブール結果を返す必要があります。

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

私はより良い(より一般的な方法)実装を探しています(おそらく式ツリーを使用していますか?)。何か提案をいただければ幸いです。ありがとうございました!

4

3 に答える 3

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コンバーターを扱うときにそこを回避する方法はありません。文字列から2つのオブジェクトを解析したので、一般的な比較を行うことができます。平等は単純です-使用するだけですObject.Equals(できれば静的なもの-nullを処理します):

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します。

于 2009-08-13T05:40:00.017 に答える
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);
    }
}
于 2009-08-13T05:55:21.873 に答える
-2

これは宿題ですか?

つまり、答えはクラス/メソッドの実装を書くことです。

あなたが抱えている具体的な問題は何ですか?正確には何がわかりませんか?

問題をいくつかの部分に分解すると、簡単になります。

于 2009-08-13T03:49:14.983 に答える