5

IEqualityComparer を実装するこのきちんとしたクラスを書いているので、任意の匿名型 (または実際にはプロパティを持つ任意の型) を渡すことができ、型のプロパティ値を比較することで自動的に型を比較します。

public class CompareProperty<T> : IEqualityComparer<T>
    {
        private Type type;
        private PropertyInfo propInfo;
        private string _fieldName;

        public string fieldName
        {
            get;
            set;
        }

        public CompareProperty(string fieldName)
        {
            this.fieldName = fieldName;
        }

        public bool Equals<T>(T x, T y)
        {
            if (this.type == null)
            {
                type = x.GetType();
                propInfo = type.GetProperty(fieldName);
            }
            object objX = propInfo.GetValue(x, null);
            object objY = propInfo.GetValue(y, null);
            return objX.ToString() == objY.ToString();
        }
    }

これは、何度も使用できる便利な小さなヘルパー関数だと思いました。

これを使用するには、次のことを行う必要があります。

var t = typeof(CompareProperty<>);
var g = t.MakeGenericType(infoType.GetType());
var c = g.GetConstructor(new Type[] {String.Empty.GetType()});
var obj = c.Invoke(new object[] {"somePropertyName"});

それはそれで十分ですが、それが返す obj 変数をどうすればよいのでしょうか?

someEnumerable.Distinct(obj);

個別の拡張関数のオーバーロードはこれを受け入れません。IEqualityComparer 型が表示されないためです。もちろん、オブジェクトのみが表示されます。

someEnumerable.Distinct((t) obj);
someEnumerable.Distinct(obj as t);

これも機能しません。型/名前空間が見つかりません (赤い下線)。

どうすればこれをまっすぐにできますか?

4

2 に答える 2

7

最初に非匿名タイプのソリューションを提供し、その後、匿名タイプでも機能するように拡張します。うまくいけば、それは人々があなたの質問へのコメントで何を言おうとしていたかを理解するのに役立つでしょう。

私の「ユニバーサル」IEqualityComparer<>は次のようになります。

public class PropertyComparer<T> : IEqualityComparer<T>
{
    private readonly PropertyInfo propertyToCompare;

    public PropertyComparer(string propertyName)
    {
        propertyToCompare = typeof(T).GetProperty(propertyName);
    }
    public bool Equals(T x, T y)
    {
        object xValue = propertyToCompare.GetValue(x, null);
        object yValue = propertyToCompare.GetValue(y, null);
        return xValue.Equals(yValue);
    }

    public int GetHashCode(T obj)
    {
        object objValue = propertyToCompare.GetValue(obj, null);
        return objValue.GetHashCode();
    }
}

最初に、次のように非匿名タイプで使用したいとしますPerson

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string Surname { get; set; }
 }

使用法は非常に簡単です。

IEnumerable<Person> people = ... ; // some database call here
var distinctPeople = people.Distinct(new PropertyComparer<Person>("FirstName"));

ご覧のとおり、を使用するには、インスタンスが相互に比較されるPropertyComparer<T>タイプ()を指定する必要があります。匿名タイプを扱うときTはどうなりますか?これらは実行時に生成されるため、コンパイル時Tがわからないという理由だけで、インスタンスを直接作成して比較機能を使用することはできません。代わりに、型推論を使用して、C#コンパイラが独自にコンテキストから推論できるTようにする必要があります。これを行うための適切な方法は、次の拡張メソッドを作成することです。T

public static class LinqExtensions
{
    public static IEnumerable<T> WithDistinctProperty<T>(this IEnumerable<T> source, string propertyName)
    {
        return source.Distinct(new PropertyComparer<T>(propertyName));
    }
}

これで、匿名タイプでも機能します。

var distinctPeople = people
        .Select(x => new { x.FirstName, x.Surname })
        .WithDistinctProperty("FirstName");

突然、クエリが処理している正確なタイプをどこでも指定する必要はありません。C#コンパイラは、コンテキスト(この場合はsource、拡張メソッド)。

これがお役に立てば幸いです。

于 2012-07-06T19:31:32.267 に答える