163

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.

svick
  • 236,525
  • 50
  • 385
  • 514
Dan Rigby
  • 17,133
  • 6
  • 43
  • 60
  • I couldn't understand @Adam's answer when I looked once but after 2mins I could get what it is, thank you for the answer. Derived class has more than one implementation may be this is the point. Anyway I want to show its notation for others. "class DerivedClass : ParentClass where Type : IType" . Nothing should be between last implemented class and where clause. – nurisezgin Apr 20 '19 at 19:15

5 Answers5

231

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

class DerivedFoo<T1, T2> : ParentFoo<T1, T2>, IFoo where T2 : IBar
{
    ...
}
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 6
    To others, I've internalized this as, a class only gets one where clause, and it goes at the end for any & all generic type constraints. – Andy V Aug 22 '16 at 16:18
  • @Visser It is allowed to have multiple where clauses, class Test where T1 : Interface1 where T2 : Interface2 – bwing Mar 06 '18 at 23:24
  • @Visser yeah, what bwing said, also each where clause can have multiple constraints… so the syntax from the original post is correct, it just means something different that the op wanted. `where T2 : IBar, IFoo ` just means that `T2` has to implement both interfaces instead of `DerivedFoo ` implementing `IFoo` – v01pe Oct 22 '18 at 10:03
16

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.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 125
    Eric, while I greatly respect you as a professional and appreciate your feedback, I can't help but be frustrated by what comes across as an abrasive answer. You are criticizing me for choosing to ask a question on a programming Q&A site over locating, downloading, and searching through a highly technical 503 page Word document buried off a link in MSDN. Thats pretty rough. This was the most efficient use of my time and has the added benefit that it may help someone else later. The link to the C# Lang Spec for those interested is: http://msdn.microsoft.com/en-us/vcsharp/aa336809.aspx – Dan Rigby Jan 05 '10 at 18:14
  • 18
    No criticism was intended. There is a pervasive bias in pure-text communication which makes simple statements of facts sound brusque and abrasive; I try to read charitably when presented with a list of helpful facts, and recommend that you do so as well. I stand by my recommendation; if you have questions about syntax, the spec answers them definitively and begins with a helpful table of contents for locating definitions of specific syntaxes. – Eric Lippert Jan 05 '10 at 18:38
  • 3
    Dan, finding C# spec is as simple as entering 'C# Spec' in Google and hitting the 'I am lucky' button. And if you are a professional C# developer, you should already have C# spec in PDF format on your machine. Also, I don't mean to criticize you either. I was not used to reading spec earlier but I have started reading it thanks to Jon, Eric and Pavel who always quotes C# spec for any question. I found that C# spec, even though it could be hard to read at times, is a great way to learn about the language. – SolutionYogi Jan 05 '10 at 18:48
  • 1
    @Eric Lippert: Fair enough. Thank you for your reply. As a constructive suggestion, it would be helpful if Microsoft would integrate the contents of the specification directly into MSDN in addition to having it exist as a separate download. The Visual Studio .Net MSDN version has an integrated version of the specification, but not later versions. I've given thought to purchasing Anders Hejlberg's book, but with .Net 4.0 around the corner I'm reluctant to just yet. http://www.amazon.com/C-Programming-Language-3rd/dp/0321562992/ Thanks. – Dan Rigby Jan 05 '10 at 18:50
  • @SolutionYogi: The document on MS's website is in .doc format (I'm aware of it's location as I linked to it in my comment), where have you located a PDF version? Thanks. – Dan Rigby Jan 05 '10 at 18:52
  • On ECMA's website. http://www.ecma-international.org/publications/standards/Ecma-334.htm – SolutionYogi Jan 05 '10 at 18:53
  • @SolutionYogi: The only catch to that is that the ECMA standard trails the Microsoft C# language specification in currency. For example, ECMA-334 does not cover LINQ or Lambdas. While it would have worked for my question, I'm more inclined to reference the most current document. – Dan Rigby Jan 05 '10 at 18:59
  • You are absolutely correct. So far, for my simple needs, the PDF version has worked. – SolutionYogi Jan 05 '10 at 19:08
  • Wow, you learn something every day...didn't know that a class definition could be followed by a semicolon (don't know why you'd want to, but I'm sure there's a reason). +1 – Adam Robinson Jan 05 '10 at 22:33
  • 3
    C++ *requires* that a class declaration end in a semicolon. Many C# developers come from a C++ background; sometimes their fingers put the semicolon in without their brains getting involved. :-) There are a number of constructs in C# which take an optional semi where C++ requires one. It's pretty much just a subtle convenience. I suppose it also lets you subtly call out when a type declaration is ending vs say a method body declaration. – Eric Lippert Jan 06 '10 at 00:21
10
public interface IFoo {}
public interface IBar {}

public class ParentFoo<T,T1> { }
public class DerivedFoo<T, T1> : ParentFoo<T, T1>, IFoo where T1 : IBar { }
Stan R.
  • 15,757
  • 4
  • 50
  • 58
3

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 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 Derived7<T> : BaseType2<string> where T : struct { }// YES!
class Derived6<T> : BaseType2<T> 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!
Stokely
  • 12,444
  • 2
  • 35
  • 23
2
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.

user875234
  • 2,399
  • 7
  • 31
  • 49