Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Generics to avoid code repetition?

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?

like image 628
Mastermnd Avatar asked Sep 23 '10 19:09

Mastermnd


2 Answers

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" :)

like image 93
Jon Skeet Avatar answered Sep 19 '22 02:09

Jon Skeet


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

like image 29
gandjustas Avatar answered Sep 20 '22 02:09

gandjustas