Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constraints on type parameters: interface vs. abstract class

Tags:

c#

.net

Creating a simple list (UniqueList) of items with a Name property that must only contains unique items (defined as having different Names). The constraint on the UniqueList type can be an interface:

interface INamed
{
    string Name { get;}
}

or an abstract class:

public abstract class NamedItem
{
    public abstract string Name { get; }
}

So the UniqueList can be:

class UniqueList<T> : List<T> where T : INamed

or

class UniqueList<T> : List<T> where T : NamedItem

The class function, AddUnique:

public T AddUnique(T item)
{
    T result = Find(x => x.Name == item.Name);
    if (result == default(T))
    {
        Add(item);
        return item;
    }
    return result;
}

If the class type constraint is based on the interface, compiling results in

Error:  Operator '==' cannot be applied to operands of type 'T' and 'T'

at the line

if (result == default(T))

All is well if I base UniqueList on the abstract class. Any thoughts?

like image 792
Ol' Badger Avatar asked Sep 20 '11 17:09

Ol' Badger


People also ask

What is the purpose of an interface constraints on a type parameter?

Interface Type Constraint You can constrain the generic type by interface, thereby allowing only classes that implement that interface or classes that inherit from classes that implement the interface as the type parameter.

What are type constraints?

A type constraint on a generic type parameter indicates a requirement that a type must fulfill in order to be accepted as a type argument for that type parameter. (For example, it might have to be a given class type or a subtype of that class type, or it might have to implement a given interface.)

Can abstract method have parameters in C#?

No, and it would be pointless to do so. If you didn't declare the parameters, you wouldn't be able to call the method given only a reference to the base class.


1 Answers

That's because the interface can be applied to a struct which is a value type. To make it work with the interface extend the constraint like this:

class UniqueList<T> : List<T> where T : INamed, class

That will make sure you won't be able to pass a struct as T and hence default(T) will evaluate to null which is what you expect.


Also, I'd recommend generalizing your UniqueList a bit, allowing for different types of unique keys:

interface IUnique<TKey>
{
    TKey UniqueKey { get;}
}

class UniqueList<TItem,Tkey> : List<TItem> where TItem : IUnique<TKey>, class

Then the INamed interface can be easily declared as:

interface INamed : IUnique<string>
{
    string Name { get;}
}

UniqueKey or Name would be implmeneted explicitly in the implementing class to prevent unnecessary (duplicate in fact) public class members.

like image 94
Ondrej Tucny Avatar answered Oct 21 '22 19:10

Ondrej Tucny