Краткий C # код для сбора нескольких свойств с ненулевым значением в коллекцию? - PullRequest
2 голосов
/ 24 мая 2010

Довольно основная проблема для изменения. Учитывая класс, такой как это:

public class X
{
    public T A;
    public T B;
    public T C;
    ...

    // (other fields, properties, and methods are not of interest here)
}

Я ищу краткий способ кодирования метода, который будет возвращать все A, B, C, ..., которые не равны нулю в перечисляемой коллекции. (Предположим, что объявление этих полей как массива не вариант.)

public IEnumerable<T> GetAllNonNullABCs(this X x)
{
    // ?
}

Очевидная реализация этого метода:

public IEnumerable<T> GetAllNonNullABCs(this X x)
{
    var resultSet = new List<T>();

    if (x.A != null) resultSet.Add(x.A);
    if (x.B != null) resultSet.Add(x.B);
    if (x.C != null) resultSet.Add(x.C);
    ...

    return resultSet;
}

Что меня особенно беспокоит, так это то, что код выглядит многословным и повторяющимся, и что я заранее не знаю начальную емкость List.

Я надеюсь, что есть более умный способ, возможно, с участием оператора ??? Есть идеи?


Примечание о выбранном ответе:

Я наконец выбрал комбинацию ответов Брайана Уоттса и dtb , что позволяет четко разделить определение набора свойств A, B, C, ... и фильтрация ненулевого подмножества:

(1) Определение набора включенных полей / свойств:

IEnumerable<T> AllABCs(this X x)
{
    return new[] { x.A, x.B, x.C, ... };
}

Или альтернативно:

IEnumerable<T> AllABCs(this X x)
{
    yield return x.A;
    yield return x.B;
    yield return x.C;
    ...
    yield break;
}

(2) Возврат только ненулевых значений:

IEnumerable<T> ThatAreNotNull(this IEnumerable<T> enumerable)
{
    return enumerable.Where(item => item != null);
}

IEnumerable<T> AllNonNullABCs(this X x)
{
    return AllABCs().ThatAreNotNull();
    //     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    //     goal reached; it won't get shorter and clearer than this, IMO!
}

Ответы [ 7 ]

7 голосов
/ 24 мая 2010
public static IEnumerable<T> GetAllNonNullAs(this X x)
{
    return new[] { x.A, x.B, x.C }.Where(t => t != null);
}
4 голосов
/ 24 мая 2010

Вы можете использовать ключевое слово yield для создания перечислимого элемента, который возвращает элементы непосредственно вместо заполнения списка:

public IEnumerable<T> GetAllNonNullAs(this X x)
{
    if (x.A != null) yield return x.A;
    if (x.B != null) yield return x.B;
    if (x.C != null) yield return x.C;
    ...
}

Чтобы избавиться от нулевых проверок, вымог бы написать метод, который возвращает все значения, и отфильтровать результат:

public IEnumerable<T> GetAllAs(this X x)
{
    yield return x.A;
    yield return x.B;
    yield return x.C;
    ...
}

public IEnumerable<T> GetAllNonNullAs(this X x)
{
    return x.GetAllAs().Where(y => y != null);
}
2 голосов
/ 24 мая 2010

Возможно, вам следовало использовать массив или другой тип коллекции при определении класса:

public class X
{
    public T[] ABC = new T[3];
    ...
}

Тогда это становится тривиальным:

return ABC.Where(x => x != null);
2 голосов
/ 24 мая 2010

LINQ на помощь!

public IEnumerable<T> GetAllNonNullAs(this X x)
{
    return from pi in x.GetType().GetProperties()
           let val = pi.GetValue(x, null)
           where val != null
           select (T) val;
}

Вы также можете добавить проверку, что значение имеет тип T, но я не уверен, является ли это проблемой для вас.

0 голосов
/ 24 мая 2010
public IEnumerable<T> GetAllNonNullAs(this X x)
{
    return this.Add(X.A, X.B, X.C);
}

private List<T> Add(params X[] items)
{
   var result = new List<T>();
   foreach(var item in items)
   {
       if(item != null)
       {
           result.Add(item);
       }
   }
}
0 голосов
/ 24 мая 2010

Я бы предложил гибрид некоторых других ответов.Используйте yield для возврата свойств, но затем используйте метод расширения .Where LINQ для фильтрации нулей.

0 голосов
/ 24 мая 2010

Для этого вы можете использовать блок итератора, хотя он не намного менее многословен.

public IEnumerable<T> GetAllNonNullAs(this X x)
{
    if(x.A != null) yield return x.A;
    if(x.B != null) yield return x.B;
    if(x.C != null) yield return x.C;
}
...