2

式ツリーを使用して、あるタイプのインスタンスのすべてのプロパティをそのタイプの別のインスタンスのプロパティと比較するメソッドを動的に生成する単純なジェネレーターを作成しようとしています。intこれは、などのほとんどのプロパティで正常に機能しますが、 (およびおそらく他のnull許容値タイプ)stringでは失敗します。DateTime?

方法:

static Delegate GenerateComparer(Type type)
{
  var left = Expression.Parameter(type, "left");
  var right = Expression.Parameter(type, "right");

  Expression result = null;

  foreach (var p in type.GetProperties())
  {
    var leftProperty = Expression.Property(left, p.Name);
    var rightProperty = Expression.Property(right, p.Name);

    var equals = p.PropertyType.GetMethod("Equals", new[] { p.PropertyType });

    var callEqualsOnLeft = Expression.Call(leftProperty, equals, rightProperty);

    result = result != null ? (Expression)Expression.And(result, callEqualsOnLeft) : (Expression)callEqualsOnLeft;
  }

  var method = Expression.Lambda(result, left, right).Compile();

  return method;

}

DateTime?プロパティでは、次のように失敗します。

タイプ'System.Nullable`1[System.DateTime]'の式は、メソッド'Boolean Equals(System.Object)'のタイプ'System.Object'のパラメーターには使用できません。

EqualsOK、それでそれはそれが期待する過負荷を見つけobjectます。それで、DateTime?それがに変換可能であるので、なぜ私はそれに渡すことができないのobjectですか?私が見るとNullable<T>、それは確かにのオーバーライドを持っていますEquals(object o)

PS:値を処理できないため、これはまだ適切なジェネレーターではないことに気付きましたが、それnullについては説明します:)

更新:Iraklisの答えはこの特定の問題に対しては機能しましたが、最終的には、私が十分だと思うはるかに単純なアプローチを採用しました。単にを使用してExpression.Equalください。私の場合の99%をカバーしていると思います(オーバーライドEqualsせずにオーバーライドを処理できるかどうかはわかりませんが、問題ありません==)。

4

2 に答える 2

2

このコードで型がnull許容であることを確認すると、機能する可能性があります。

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)){}  

コードサンプルはここ
からです そしてそれらがNullableであるなら、あなたは呼び出すことができます

Nullable.Equals<T>(T? n1, T? n2);
于 2011-01-07T17:49:09.153 に答える
0

使えるものをウェブで探した後、自分でも実装することにしました。式ツリーは使用しませんでした。代わりに、リフレクションを使用してすべてのプロパティをスキャンし、ToString()それらを比較するために使用します。プロパティがコレクションの場合、コレクション内のすべての要素が等しいかどうかを比較します。

これがコードです。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

namespace Utils
{
    public class PropertyComparer<T> : IEqualityComparer<T>
    {
        public bool Equals(T x, T y)
        {
            IEnumerable<PropertyInfo> allProperties = typeof(T).GetProperties();
            foreach(PropertyInfo pi in allProperties)
            {
                if (pi.GetCustomAttributes<EqualityIrrelevantAttribute>().Any())
                {
                    continue;
                }

                object xProp = pi.GetValue(x);
                object yProp = pi.GetValue(y);

                if ((xProp == null) && (yProp == null))
                {
                    continue;
                }
                else if ((xProp == null) || (yProp == null))
                {
                    return false;
                }
                else if (xProp is ICollection)
                {
                    if (!CollectionsEqual(xProp as ICollection, yProp as ICollection))
                    {
                        return false;
                    }
                }

                if (xProp.ToString() != yProp.ToString())
                {
                    return false;
                }
            }

            return true;
        }

        bool CollectionsEqual(ICollection left, ICollection right)
        {
            IEnumerator leftEnumerator = left.GetEnumerator();
            IEnumerator rightEnumerator = right.GetEnumerator();

            bool leftAdvanced = leftEnumerator.MoveNext();
            bool rightAdvanced = rightEnumerator.MoveNext();

            if ((leftAdvanced && !rightAdvanced) || (rightAdvanced && !leftAdvanced))
            {
                return false;
            }
            else if (!leftAdvanced && !rightAdvanced)
            {
                return true;
            }

            bool compareByClass = false;
            object comparer = null;
            MethodInfo equalsMethod = null;

            // Inspect type first
            object peek = leftEnumerator.Current;
            Type valuesType = peek.GetType();
            if (valuesType.IsClass)
            {
                compareByClass = true;
                Type comparerType = typeof(PropertyComparer<>).MakeGenericType(new Type[] { valuesType });
                equalsMethod = comparerType.GetMethod("Equals", new Type[] { valuesType, valuesType });
                comparer = Activator.CreateInstance(comparerType);
            }


            leftEnumerator.Reset();
            rightEnumerator.Reset();

            while (true)
            {
                leftAdvanced = leftEnumerator.MoveNext();
                rightAdvanced = rightEnumerator.MoveNext();

                if ((leftAdvanced && !rightAdvanced) || (rightAdvanced && !leftAdvanced))
                {
                    return false;
                }
                else if (!leftAdvanced && !rightAdvanced)
                {
                    return true;
                }

                object leftValue = leftEnumerator.Current;
                object rightValue = rightEnumerator.Current;

                if (compareByClass)
                {
                    bool result = (bool)equalsMethod.Invoke(comparer, new object[] { leftValue, rightValue });
                    if (!result)
                    {
                        return false;
                    }
                }
                else if (leftEnumerator.Current.ToString() != rightEnumerator.Current.ToString())
                {
                    return false;
                }

                // Continue looping
            }
        }

        public int GetHashCode(T obj)
        {
            throw new NotImplementedException();
        }
    }
}

クラスのプロパティ自体がクラスである場合、そのクラスのプロパティを比較できる新しい比較子が作成されます。オプションで、を使用して比較から除外する特定のプロパティをマークすることもできますEqualityIrrelevantAttribute。それは私にとって本当にうまくいきます、私は他の人がそれが役に立つと思うことを願っています。

于 2015-11-06T22:24:29.713 に答える