13

ここで達成しようとしているのは、ボックス化されたプリミティブ型の単純な値の比較です。

((object)12).Equals((object)12); // Type match will result in a value comparison,
((object)12).Equals((object)12d); // but a type mismatch will not. (false)
object.Equals((object)12,(object)12d); // Same here. (false)

「理由」はわかります。「方法」がわかりません。

型は実行時まで不明であり、データソースのプリミティブ型である可能性があります。これには、文字列、日付時刻、ブール値などが含まれます。両方の型を処理する拡張メソッドを作成し、'==' 比較を行う前にキャストするという醜いルートをたどりました: (完全を期すために、すべてのプリミティブ型を含めました。 、および私が興味を持っていたもの)

public static bool ValueEquals(this object thisObj, object compare)
    {
        if (thisObj is int)
        {
            int obj = (int)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            if (compare is decimal)
                return (obj == (decimal)compare);
            if (compare is float)
                return (obj == (float)compare);
            <... and so on for each primitive type ...>
        }
        if (thisObj is uint)
        {
            uint obj = (uint)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            if (compare is uint)
                return (obj == (uint)compare);
            <... Again for each primitive type ...>
        }
        if (thisObj is decimal)
        {
            decimal obj = (decimal)thisObj;
            if (compare is int)
                return (obj == (int)compare);
            <... Etc, etc ...>

結果として得られたメソッドは 300 行以上の長さであることが判明しましたが、これは問題ありませんでしたが (それでも恐ろしく)、「==」以上のことを行う必要があります。>、<、<=、>=、!= が必要です。

ボックス化された値の型の比較に使用できるリフレクションに何かありますか?

まったく何か?

4

2 に答える 2

8

arg1の型が変換したい型であると想定しているように見えるので、このようなgenricを使用します。arg2がIConvertible(int、double、すべての数値、文字列などがすべてIConvertible)である限り、これは機能します。

public static bool ValueEquality<T1, T2>(T1 val1, T2 val2) 
    where T1 : IConvertible 
    where T2 : IConvertible
{
    // convert val2 to type of val1.
    T1 boxed2 = (T1) Convert.ChangeType(val2, typeof (T1));

    // compare now that same type.
    return val1.Equals(boxed2);
}

**更新**両方のタイプを汎用引数にしました。両方を推測でき、arg2のコンパイル時の安全性を高めて、コンパイル時にIConvertibleであることを確認します。

このジェネリック関数を指定すると、次のすべてがtrueを返します(最初の引数から推測されるため、型引数を指定する必要はありません。

        Console.WriteLine(ValueEquality(1, "1"));
        Console.WriteLine(ValueEquality(2, 2.0));
        Console.WriteLine(ValueEquality(3, 3L));

アップデート

あなたのコメントに基づいて、あなたが持っているのがオブジェクトだけである場合、ここにオーバーロードがあります。両方が共存でき、引数に基づいてより適切なものを呼び出します。

    public static bool ValueEquality(object val1, object val2)
    {
        if (!(val1 is IConvertible)) throw new ArgumentException("val1 must be IConvertible type");
        if (!(val2 is IConvertible)) throw new ArgumentException("val2 must be IConvertible type");

        // convert val2 to type of val1.
        var converted2 = Convert.ChangeType(val2, val1.GetType());

        // compare now that same type.
        return val1.Equals(converted2);
    }

そして、これはオブジェクトに対して機能します:

        object obj1 = 1;
        object obj2 = 1.0;

        Console.WriteLine(ValueEquality(obj1, obj2));

私が言ったように、これらは両方ともオーバーロードとして共存できるため、互換性のあるIConvertibleタイプを直接比較すると、ジェネリックが使用され、ボックス化されたタイプがオブジェクトとして存在する場合は、オブジェクトのオーバーロードが使用されます。

于 2011-07-12T18:17:30.157 に答える
3

手動の代わりにIComparableを使用することを検討してください-http ://msdn.microsoft.com/en-us/library/system.icomparable.compareto.aspx

将来、同様の何かが必要な場合は、最初に1つのオペランドの型に切り替えて、のような操作を処理するメソッドを使用して、型ごとに「操作ハンドラー」クラスを実装することを検討してIntOpHandler.PerformOp(int left, object right)ください。

また、多くの場合、最初に複数の型をマージすることで、処理する必要のある型の数を減らすことができます(つまり、byte、short、ushort、int、uint、long-最初にlongにキャストしてから、longで操作を実行します)。

于 2011-07-12T18:18:04.993 に答える