1

I'm trying to write a code that not only is easy to read, but also flexible. To do so I'm using interfaces more often. For instance, instead of having a List as parameter of a method, I'm using IEnumerable whenever possible.

But check this method

public static Color MeanColor(this IEnumerable<Color> list) {
        var colors = list as Color[] ?? list.ToArray();
        if (!colors.Any()) {
            // Gray is the mean of all colors (Black+White/2)
            return Color.FromArgb(127, 127, 127);
        }

        var r = 0;
        var g = 0;
        var b = 0;
        foreach (var c in colors) {
            r += c.R;
            g += c.G;
            b += c.B;
        }

        return Color.FromArgb(r / colors.Length, g / colors.Length, b / colors.Length);
    }

Since I need to use the Length method/property, I can't just use a IEnumerable. Therefore I check if it's a array, and if it's not I convert it to one using the ToArray() method.

I tried using a 'count' variable inside the foreach loop, like this

int count = 0;
foreach (var c in colors) {
            r += c.R;
            g += c.G;
            b += c.B;
            count++;
        }

But the performance hit is greater than converting a 50000 element List to and array.

So I'd like to know: is it possible to 'ask an object' if it has a method "X'? Is this case, "ask" the object if it has a Count property or GetLength() method.

edit: Tim's answer does solve my problem, in this case. But the question remains, is it possible to ask an object has a specific method/property?

Trauer
  • 1,981
  • 2
  • 18
  • 40
  • I think passing in an IEnumerable and then casing it to an array of Color or converting it to an array explicitly is a fundamental issue in this case. – Darren Oct 15 '14 at 22:03
  • Generalizing method parameters is good. However, in this case maybe you went too far. If you use ICollection, then you are assured of a Count property. Alternatively, at the very least in your original implementation you might consider converting the input to ICollection, since that will succeed for other collection types besides an array. Of course, Tim's suggestion to use Enumerable.Count is a good one too. It really just depends on exactly what kind of constraint you want to make on the caller, and how flexible you want your own method to be. – Peter Duniho Oct 15 '14 at 22:12
  • Copying it to an array is like asking yourself "how many pages long is this book that has no page numbers? I'll just write out a copy by hand into a book that does have page numbers". Just count the pages! There's no need to make an entire copy. – Eric Lippert Oct 15 '14 at 22:24
  • @EricLippert: in general, you're right. But here, the input enumeration has to be enumerated again. If it could be expensive to do so (one thing about IEnumerables is that you have no idea where they came from and they could in fact be costly to enumerate), then copying it immediately to some other collection (list, array, whatever) can be more efficient. There's not enough context in the question to know for sure what the best way is. – Peter Duniho Oct 15 '14 at 22:30
  • @EricLippert @PeterDuniho Using an `ICollection` should also ensure that the input is actually finite, while it's perfectly acceptable for an `IEnumerable` to be infinite (e.g., because it's actually a network streak). ICollection is somewhat of an implicit contract that says "The collection is finite and thus actually has a count". – Michael Stum Oct 15 '14 at 23:16

2 Answers2

4

You could use Enumerable.Count:

public static Color MeanColor(this IEnumerable<Color> list) {
    int count = list.Count();
    // ...
    return Color.FromArgb(r / count, g / count, b / count);
}

Note that this will execute a query completely if it's not a collection, therefore i've used the variable to do it only once.

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • A point about caching the count. IMHO, if enumeration of the collection could be a performance concern, it's better to just convert to a list from the outset. Then you get the count "for free" and don't have to re-enumerate the original collection later in the method. – Peter Duniho Oct 15 '14 at 22:02
  • That does solve my problem, thank you. It does exactly what I wanted to do. But that doesn't answer my question, for a general case. Is it possible to 'ask' a object if it has a specific method? – Trauer Oct 15 '14 at 22:04
  • @Trauer: yes, it is possible to "ask" an object if it has a specific method. You can use either reflection, or assign to a dynamic variable and attempt to use the method you want (which will cause an exception if it doesn't exist). Both are very slow though. It is much more efficient to check for interface implementations than specific method implementations. – Peter Duniho Oct 15 '14 at 22:14
  • Thanks Peter. I have heard of Reflection before. Guess it's time to study it a little. – Trauer Oct 15 '14 at 22:15
  • @PeterDuniho: i've deleted the comment since it was incorrect. Actually `Enumerable.Count` is also optimized to use `ICollection.Count` if possible. I've looked at [the wrong overload](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs#d35db0dea6ae310a#references). – Tim Schmelter Oct 15 '14 at 22:23
  • @TimSchmelter: thanks. Glad to see you're back to your original reply, which I thought was quite good already. :) I've gone back and cleaned up my side of our back-and-forth. – Peter Duniho Oct 15 '14 at 22:28
  • @Trauer: you could use reflection. But in this case you could simply check if it can be casted to ICollection(or ICollecton) which every collection implements(even an array has a Count property if you cast it). However, Enumerable.Count is already optimized in this way. – Tim Schmelter Oct 15 '14 at 22:54
1

It is possible to ask for an object property existence by using .net reflection, which is the ability for the code to analyse objects and classes. In your case, by using the GetMethod method on a type, you can see what methods exist or not by checking it against null.

MethodInfo mInfo = typeof(Program).GetMethod("MethodA");
var methodExists = mInfo != null;

The same principle exists for constructors, fields, properties, and more (GetConstructor, GetField, GetProperties, ...)

Finding some methods such as reflection methods is more complicated, but you can have some pointers on this very site

Community
  • 1
  • 1
samy
  • 14,832
  • 2
  • 54
  • 82
  • @PeterDuniho Yes, I wrote `IEnumerable` when I meant `IEnumerable`. But since it is from extension methods, it is not considered part of the interface itself... removing my last part – samy Oct 15 '14 at 22:29