views:

133

answers:

7

A fairly basic problem for a change. Given a class such as this:

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

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

I am looking for a concise way to code a method that will return all A, B, C, ... that are not null in an enumerable collection. (Assume that declaring these fields as an array is not an option.)

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

The obvious implementation of this method would be:

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;
}

What's bothering me here in particular is that the code looks verbose and repetitive, and that I don't know the initial List capacity in advance.

It's my hope that there is a more clever way, probably something involving the ?? operator? Any ideas?


Note about the chosen answer:

I finally went for a mix of both Bryan Watts' and dtb's answers that allows for a clear separation of defining the set of properties A,B,C,... and the filtering of the non-null subset:

(1) Definition of the set of included fields/properties:

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

Or alternatively:

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

(2) Returning only non-null values:

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!
}
+3  A: 

You can use the yield keyword to create an enumerable that returns the elements directly instead of populating a list:

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;
    ...
}

To get rid of the null checks, you could write a method that returns all values, and filter the result:

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);
}
dtb
Very nice (the combination of `yield` and `.Where` LINQ query)! That's the kind of code improvement I was looking for!
stakx
+2  A: 

LINQ to the rescue!

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;
}

You might also want to add a check that the value is of type T, but I'm not sure whether tha's an issue for you.

Dean Harding
This sacrifices performance to a very large degree simply not to have to specify each property.
Adam Robinson
Yes, but the "..." after `A`, `B`, `C`, implies there are more than just three properties. This will work for any number of properties.
Dean Harding
Also this will loop though ALL properties. If you have specific ones that should not be part of this youre doomed.
bitbonk
The requirement was "code a method that will return all A, B, C, ... that are not null in an enumerable collection" - that's exactly what this method does. There were no requirements on performance, no requirements on only a specific subset. Why write 15 lines of code for 15 properties when 4 will suffice?
Dean Harding
Actually, there might be other fields/properties that should not be included; that's why I put the *"other fields, properties, ... not of interest"* bit in the code.
stakx
A: 

You can use an iterator block for this, though it's not much less verbose.

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;
}
Adam Robinson
+2  A: 

Perhaps you should have used an array or other collection type when defining your class:

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

Then it becomes trivial:

return ABC.Where(x => x != null);
Mark Byers
As I said, that's not an option. I only named the fields `A`,`B`,`C`,`...` for demonstration purposes, but in my real class, they'd have more distinct names that can't and shouldn't be subsumed in an array.
stakx
@stakx: Oh I missed that bit. But note that you can still use this same technique even if you can't change the class by creating the array from scratch each time you need it. Which is what the answer you've accepted does.
Mark Byers
(That's right. I first thought a `yield` solution would be best, but after seeing how very brief an array-based solution can be, I changed my mind.)
stakx
A: 

I would suggest a hybrid of some of the other answers. Use yield to return the properties, but then use the .Where LINQ extension method to filter out the nulls.

Eric Mickelsen
+6  A: 
public static IEnumerable<T> GetAllNonNullAs(this X x)
{
    return new[] { x.A, x.B, x.C }.Where(t => t != null);
}
Bryan Watts
Accepted because your answer is so wonderfully short and clear. Perhaps for a large number of properties, a solution involving `yield` would be preferable because of a smaller memory footprint.
stakx
A: 
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);
       }
   }
}
bitbonk