6

例として、クラス定義から始めましょう。

public class Person
{
    public string FirstName;
    public string LastName;
    public int Age;
    public int Grade;
}

ここで、 3つのオブジェクトを含むList<Person>呼び出しがあると仮定します。people

{"Robby", "Goki", 12, 8}
{"Bobby", "Goki", 10, 8}
{"Sobby", "Goki", 10, 8}

私が探しているのは、次の単一のPersonオブジェクトを取得する方法です。

{null, "Goki", -1, 8}

ここで、すべてのオブジェクトで同じフィールドは値を保持しますが、複数の値を持つフィールドは無効な値に置き換えられます。

私の最初の考えは次のとおりです。

Person unionMan = new Person();
if (people.Select(p => p.FirstName).Distinct().Count() == 1)
    unionMan.FirstName = people[0].FirstName;
if (people.Select(p => p.LastName).Distinct().Count() == 1)
    unionMan.LastName = people[0].LastName;
if (people.Select(p => p.Age).Distinct().Count() == 1)
    unionMan.Age = people[0].Age;
if (people.Select(p => p.Grade).Distinct().Count() == 1)
    unionMan.Grade = people[0].Grade;

残念ながら、実際のビジネスオブジェクトには4つよりも多くのメンバーがあり、これは書くのが面倒であり、他の誰かが初めて見るのは大変です。

また、リフレクションを利用して、これらの反復的なチェックと割り当てをループに入れることも検討しました。

string[] members = new string[] { "FirstName", "LastName", "Age", "Grade" };
foreach (string member in members)
{
    if (people.Select(p => p.**member**).Distinct().Count() == 1)
        unionMan.**member** = people[0].**member**;
}

**メンバー**は、ただし、リフレクションにより、その特定のメンバーの取得と保存が可能になります(可能な場合)。

最初の解決策は機能し、2番目の解決策は機能すると思いますが、この問題に対するより良い代替解決策はありますか?そうでない場合、上記のように反射を使用することは可能でしょうか?

4

3 に答える 3

5

個別のメンバーをカウントするためだけにすべての値を個別に実行するのは非効率的です。最初のアイテムのメンバーと同じ値を持たない後続のアイテムのいずれかで1つの値を見つけることは、その列の状態が無効であることを意味するショートカットシナリオがあります。

このようなものは機能するはずですが、メンバーのいずれかが配列である場合、再帰的評価または他のより複雑なロジックが必要な場合は、さらに作業を行う必要があります(これはテストしていません)。

public static T UnionCombine<T>(this IEnumerable<T> values) where T : new() {
    var newItem = new T();
    var properties = typeof(T).GetProperties();
    for (var prop in properties) {
        var pValueFirst = prop.GetValue(values.First(), null);
        var useDefaultValue = values.Skip(1).Any(v=>!(Object.Equals(pValueFirst, prop.GetValue(v, null))));
        if (!useDefaultValue) prop.SetValue(newItem, pValueFirst, null);
    }
    return newItem;
}
于 2012-08-02T20:49:11.213 に答える
2

あなたの最後のアイデアは私には良いようです、このようなもの:

List<Person> persons = new List<Person>()
{
    new Person(){ FirstName="Robby", LastName="Goki", Age=12, Grade=8},
    new Person(){ FirstName="Bobby", LastName="Goki", Age=10, Grade=8},
    new Person(){ FirstName="Sobby", LastName="Goki", Age=10, Grade=8},
};

var properties = typeof(Person).GetProperties();

var unionMan = new Person();
foreach (var propertyInfo in properties)
{
    var values = persons.Select(x => propertyInfo.GetValue(x, null)).Distinct();
    if (values.Count() == 1)
        propertyInfo.SetValue(unionMan, propertyInfo.GetValue(persons.First(), null), null);
}

いくつかの観察:

  • クラスメンバーはパブリックメンバーではなくプロパティとして定義する必要があり、getアクセサーとsetアクセサーの両方がパブリックである必要があります
  • デフォルトのコンストラクターは「無効な」値を定義する必要があります(@RaphaëlAlthausによって正しく提案されています)

したがって、クラスPersonは次のようになります。

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public int Grade { get; set; }
    public Person()
    {
        this.FirstName = null;
        this.LastName = null;
        this.Age = -1;
        this.Grade = -1;
    }
}
于 2012-08-02T20:42:01.583 に答える
1

更新: Personクラスを制御できず、状態はプロパティではなくパブリックフィールドで定義されるため、これに対処するためにソリューションを更新しました。

リフレクションの使用をお勧めします。LINQクエリのエントリごとに取得するのではなく、 FieldInfo(またはPropertyInfo )オブジェクトを事前に取得することをお勧めします。Type.GetFieldType.GetPropertyを使用して取得できます。それらを取得したら、FieldInfo / PropertyInfo.GetValueおよびFieldInfo / PropertyInfo.SetValueを使用できます。

例えば:

Type personType = typeof(Person);
foreach(string member in members)
{   // Get Fields via Reflection
    FieldInfo field = peopleType.GetField(member);
    if(field != null)
    {
        if (people.Select(p => field.GetValue(p, null) ).Distinct().Count() == 1)
        {
            field.SetValue(unionMan, field.GetValue(people[0], null), null);
        }
    }
    else // If member is not a field, check if it's a property instead
    {   // Get Properties via Reflection
        PropertyInfo prop = peopleType.GetProperty(member);
        if(prop != null)
        {
            if (people.Select(p => prop.GetValue(p, null) ).Distinct().Count() == 1)
            {
                prop.SetValue(unionMan, prop.GetValue(people[0], null), null);
            }
        }
    }
}

ご指摘のとおり、デフォルトのコンストラクターで「無効な」vlauesを既に設定しているため、このループ内でそれらについて心配する必要はありません。

注:私の例では、パラメーターを受け取らないバージョンGetFieldとのバージョンを使用しました。これらはパブリックメンバーのみを返します。GetPropertiesBindingFlags

于 2012-08-02T20:42:15.517 に答える