views:

186

answers:

2

I am fairly new to C# coming from Java, and I'm wondering if there's a simple way to avoid code repetition involving primitive types like this:


private Boolean AtLeastOneBufferItemIsNonZero(int[] Buffer)
{
    Boolean result = false;
    foreach (int Item in Buffer)
    {
        result = !(Item == (int)0);
        if (result) break;
    }
    return result;
}

private Boolean AtLeastOneBufferItemIsNonZero(float[] Buffer)
{
    Boolean result = false;
    foreach (float Item in Buffer)
    {
       result = !(Item == (float)0);
       if (result) break;
    }
    return result;
}

I can't find a "Number" supertype so that I can compare "Item" in a generics implementation (I wouldn't mind the performance penalty of boxing, although I understand that in .NET there is no such thing?):


//SOMETHING LIKE THIS?
private Boolean AtLeastOneBufferItemIsNonZero<T>(T[] Buffer) where T : NUMBERTYPE
{
    Boolean result = false;
    foreach (T Item in Buffer)
    {
       result = !(Item.Equals(0)); //Nope....
       if (result) break;
    }
    return result;
}

Is the only way to create my own Number implementation and having a compare() method? That sounds like overkill doesn't it?

+11  A: 

LINQ makes this pretty simple to do, by relying on the fact that the default value of any numeric type is zero, and they have appropriate equality methods:

private bool AtLeastOneBufferItemIsNonZero<T>(T[] items)
{
    T zero = default(T);
    EqualityComparer<T> comparer = EqualityComparer<T>.Default;
    return items.Any(t => !comparer.Equals(t, zero));
}

Now that doesn't restrict it to numeric types, but it does avoid repetition. You can go further, by generalizing it to IEnumerable<T> and making it an extension method:

public static class Extensions
{
    public static bool ContainsNonDefaultValue<T>(this IEnumerable<T> source)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        T zero = default(T);
        EqualityComparer<T> comparer = EqualityComparer<T>.Default;
        return items.Any(t => !comparer.Equals(t, zero));
    }
}

You could restrict this to value types by changing the constraint to

where T : struct

but that would be a bit pointless IMO. With the change to use EqualityComparer<T>.Default, you can also use the method to check whether any value in a reference type sequence is non-null.

EDIT: As a side note, another way of look at it is to reverse the condition:

return !items.All(t => comparer.Equals(t, zero));

It depends whether you're happier with the concept of "any of them is non-zero" or "they're not all zero" :)

Jon Skeet
But that returns a value indicating whether at least one item *is* zero?
Fredrik Mörk
@Fredrik: Doh, yes - fixing...
Jon Skeet
But shouldn't that then be named `AtLeastOneBufferItemIsZero`?
Jim Mischel
@Jim: Yes, hence Fredrik's comment. Fixing :)
Jon Skeet
yeah, should be source.Any(t => t != default(T))...
Stuart
@Stuart: That won't work; you can't apply != with generic type parameters unless it's constrained to be a class type.
Jon Skeet
Thank you all for the help. I will have a look at LINQ ASAP
Mastermnd
Jon: I'm going to buy your book, it looks perfect for my needs :)
Mastermnd
@Mastermnd: Excellent - hope you enjoy it :)
Jon Skeet
A: 
private Boolean AtLeastOneBufferItemIsNonZero<T>(T[] Buffer)
{
    Boolean result = false;
    foreach (T Item in Buffer)
    {
       result = !Item.Equals(default(T)); //Yep!!!
       if (result) break;
    }
    return result;
}

PS. Use Linq

gandjustas