Inherit from a generic base class, apply a constraint, and implement an interface in C#

C#GenericsInheritanceInterfaceConstraints

C# Problem Overview


This is a syntax question. I have a generic class which is inheriting from a generic base class and is applying a constraint to one of the type parameters. I also want the derived class to implement an interface. For the life of me, I cannot seem to figure out the correct syntax.

This is what I have:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar { ... }

The first thing that came to mind was this:

DerivedFoo<T1,T2> : ParentFoo<T1, T2> where T2 : IBar, IFoo { ... }

But that is incorrect as that causes T2 to need to implement both IBar and IFoo, not DerivedFoo to implement IFoo.

I've tried a bit of Googling, use of colons, semicolons, etc, but I've turned up short. I'm sure the answer is head slappingly simple.

C# Solutions


Solution 1 - C#

You include the entire signature of your class before you define generic constraints.

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
    ...
}

Solution 2 - C#

My recommendation: when you have a question about the syntax of the C# language, read the specification; that's why we publish it. You'll want to read section 10.1.

To answer your specific question, the order of things in a class declaration is:

  • attributes, in square brackets
  • modifiers ("public", "static", and so on)
  • "partial"
  • "class"
  • the class name
  • a comma-separated list of type parameter declarations inside angle brackets
  • a colon followed a comma-separated list of base types (base class and implemented interfaces, base class must go first if there is one)
  • type parameter constraints
  • the body of the class, surrounded by braces
  • a semicolon

Everything on that list is optional except for "class", the name, and the body, but everything must appear in that order if it appears.

Solution 3 - C#

public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }

Solution 4 - C#

public class KeyAndValue<T>
{
    public string Key { get; set; }
    public virtual T Value { get; set; }
}

public class KeyAndValue : KeyAndValue<string>
{
    public override string Value { get; set; }
}

This is an extension off the existing answers. It defaults to string if you don't supply a type. I didn't implement an interface but that shouldn't require anything different than usual.

Solution 5 - C#

GENERIC INHERITANCE RULES

There are additional rules you need to follow when inheriting Generic type classes in C#. Remember, your base and derived classes can use different generic types or share them. The following tests have helped me understand the rules. Generic classes can inherit from concrete, closed constructed, or open constructed base classes as follows:

// First create some Base Classes to inherit from.
// Non-generic and generic classes are included.
// Below, I will inherit these in Derived Classes.
// If my inheritance test works, you will see "YES", otherwise "NO".

class BaseType1 { }
class BaseType2<T> { }
class BaseType3<T1,T2> { }

// -------------------------------------------------

// Concrete type inheritance. Here the inheriting type does NOT have to be generic as the child

class Derived1 : BaseType1 { }// YES!
class Derived2<T> : BaseType1 { }// YES!

// Open constructed type generic inheritance allows shared generic type inheritance.
class Derived3<T> : BaseType2<T> { }// YES!

// Closed constructed type inheritance. Note that what the child class accepts as far as types is different from what the parent accepts. This is allowed allowed as each implements its own types and constraints on its members and the parent has defined its type.
class Derived4<T> : BaseType2<int> { }// YES!

// NO! Base type must know its accepted type if the child class is not accepting a generic "T" type as well.
//class Derived5 : BaseType2<T> { }// NO!

// Another form that limits generic type. Note, here the first base type uses a reference type "string" that is different from what the derived child type uses, which is any "struct" type or int, etc. Because the base type is defined as "string" the child can define anything for its generic or not even use a generic.

// The "where" clause only affects the derived class type!
class Derived6<T> : BaseType2<T> where T : struct { }// YES!
class Derived7<T> : BaseType2<string> where T : struct { }// YES!

// NO! The "where" clause cannot be used to define the base class's type!
//class Derived8 : BaseType2<T> where T : struct { }// NO!


// NO! "T1" and "T2"must be a concrete type again if the derived type doesn't use the same
//class Derived9 : BaseType3<T1,T2>// NO!

// Here, both parent and child classes must accept one generic type for this to work.
class Derived10<T> : BaseType3<T,T> { }// YES!

// Each generic type must have unique names in the derived class AND match the base class names. That is why these fail.
//class Derived11<T, T> : BaseType3<T1, T2> { }// NO!
//class Derived12<name1, name2> : BaseType3<T1, T2> { }// NO!

class Derived13<T1, T2> : BaseType3<T1, T2> { }// YES!

// As long as one generic name matches between child and parent, this shared generic type on child and parent ok.
class Derived14<Tname,T2> : BaseType2<Tname> { }// YES!

// Again, the child class doesn't have to be generic if the base generic class has defined the types it accepts.
class Derived15 : BaseType3<string, int> { }// YES!

class Derived16<T1, T2> : BaseType3<string, int> { }// YES!

// Here, you can have the child class accept two generic types, while the parent uses its own explicit type.
class Derived17<T1, T2> : BaseType2<string> { }// YES!

// No, you need to define these using "where".
//class Derived18<string, int> : BaseType1 { }// NO!

class Derived19<T1, T2> : BaseType1 where T1 : class where T2 : struct { }// YES!

// This generates an error. You cannot inherit a generic type.
// class Derived20 : T {}// NO!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDan RigbyView Question on Stackoverflow
Solution 1 - C#Adam RobinsonView Answer on Stackoverflow
Solution 2 - C#Eric LippertView Answer on Stackoverflow
Solution 3 - C#Stan R.View Answer on Stackoverflow
Solution 4 - C#user875234View Answer on Stackoverflow
Solution 5 - C#StokelyView Answer on Stackoverflow