简体   繁体   中英

Do we really need generic class with type constraint?

Sorry if title of question is a little bit vague.
Suppose we have the following code

class ReportManager<T> where T : IPrint
{
    public void MakePaperCopy(T t)
    {
        //...
        t.Print();
        //...
    }
}

public interface IPrint
{
    void Print();
}

It can be easily remade to non-generic version. Like this

class ReportManager
{
    public void MakePaperCopy(IPrint print)
    {
        //...
        print.Print();
        //...
    }
}

Are there some advantages in first case ? it seems no.
Can i say that every generic class with single where T: ISomeInterface can be easily remade to non-generic version and it should be done because it reduces the complexity of code ?

It's not useless in all cases.
If you want to return this T , then you may want to know the exact type.
Because when you are returning it, you can access to all the (accessible) members from the derived class, not only those from the base class/interface.

In the first place, generics avoid a lot of casts. This can ends up in significant performance gains in intensive applications and services.

Secondly, it's not the same defining that a generic type parameter accepts any implementation of IPrint (non-generic) than that the whole type will handle objects which are forced to implement IPrint (generic).

Try to work on the following type requirement without generics:

  • I want that T to be IPrint and inherit PrintablePaper .

Without generics, sadly, this would be implemented using reflection:

// Ugly as hell. And error prone, because it's verified during
// run-time...
if(typeof(IPrint).IsAssignableFrom(typeof(T)) && typeof(T).IsSubclassOf(typeof(PrintablePaper)) 
{
}

With generics: T : PrintablePaper, IPrint and try to give a wrong type as generic argument, and compiler will say: slap in your face, slap in your face, ... .

Basically, in terms of high-level programming, generics were introduced to enforce strongly-typing in C# even more . Generic constraints provide details to what would be an acceptable generic argument during compile-time. That is, generics are a good way to close a software architecture/design to grow based on specs defined by generic constraints.

About the whole repository sample, if your report manager will never need to handle specializations of IPrint , generics are useless in that particular case: accepting implementations of IPrint or a type which mandatorily will implement IPrint is almost the same in your case.

In your case, it would be useful if you would need to inherit that class :

class ReportManager<T> where T : IPrint
{
    public void MakePaperCopy(T t)
    {
        //...
        t.Print();
        //...
    }
}

class SpecializedReportManager : ReportManager<FancyReport>
{
    // Beat this without generics!!
    // In inherited classes, you can work with the IReport
    // implementation directly and without casts!
    public void MakePaperCopy(FancyReport t)
    {
        //...
        t.Print();
        //...
    }
}

The generic form will prevent boxing in case of value types.

Also, you can have T implement multiple interfaces:

class ReportManager<T> where T : IPrint, IConvertible, IAnotherInterface

The 2nd form will obviously not work in that case.

After commenting on romain-aga 's post - there's yet another reason, on top of what everyone else said. If you have a constraint referencing a generic interface (say where T : IComparable<T> ) you simply won't be able to remake it into a non-generic version.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM