Is it good practice to inherit from generic types?

Is it better to use List<string> in type annotations or StringList where StringList

class StringList : List<String> { /* no further code!*/ }

I ran into several of these in Irony.

9

There is another reason why you may want to inherit from a generic type.

Microsoft recommend avoiding nesting generic types in method signatures.

It is a good idea then, to create business-domain named types for some of your generics. Instead of having a IEnumerable<IDictionary<string, MyClass>>, create a type MyDictionary : IDictionary<string, MyClass> and then your member becomes an IEnumerable<MyDictionary>, which is much more readable. It also allows you to use MyDictionary in a number of places, increasing the explicitness of your code.

This may not sound like a huge benefit, but in real-world business code I’ve seen generics that will make your hair stand on end. Things like List<Tuple<string, Dictionary<int, List<Dictionary<string, List<double>>>>>. The meaning of that generic is in no way obvious. Yet, if it were constructed with a series of explicitly created types the intention of the original developer would likely be a lot clearer. Further still, if that generic type needed to change for some reason, finding and replacing all instances of that generic type might be a real pain, compared to changing it in the derived class.

Finally, there is another reason why someone might wish to create their own types derived from generics. Consider the following classes:

public class MyClass
{
    public ICollection<MyItems> MyItems { get; private set; }
    // plumbing code here
}

public class MyOtherClass
{
    public ICollection<MyItems> MyItemCache { get; private set; }
    // plumbing code here
}

public class MyConsumerClass
{
    public MyConsumerClass(ICollection<MyItems> myItems)
    {
        // use the collection
    }
}

Now how do we know which ICollection<MyItems> to pass into the constructor for MyConsumerClass? If we create derived classes class MyItems : ICollection<MyItems> and class MyItemCache : ICollection<MyItems>, MyConsumerClass can then be extremely specific about which of the two collections it actually wants. This then works very nicely with an IoC container, which can resolve the two collections very easily. It also allows the programmer to be more accurate – if it makes sense for MyConsumerClass to work with any ICollection, they can take the generic constructor. If, however, it never makes business sense to use one of the two derived types, the developer can restrict the constructor parameter to the specific type.

In summary, it is often worthwhile to derive types from generic types – it allows for more explicit code where appropriate and helps readability and maintainability.

3

In principle there’s no difference between inheriting from generic types and inheriting from anything else.

However, why on earth would you derive from any class and not add anything further?

9

I don’t know C# very well, but I believe this is the only way to implement a typedef, which C++ programmers commonly use for a few reasons:

  • It keeps your code from looking like a sea of angle brackets.
  • It lets you swap out different underlying containers if your needs change later, and makes it clear you reserve the right to do so.
  • It lets you give a type a name that is more relevant in context.

They basically threw away the last advantage by choosing boring, generic names, but the other two reasons still stand.

9

I can think of at least one practical reason.

Declaring non-generic types that inherit from generic types (even without adding anything), allows those types to be referenced from places where they would otherwise be unavailable, or available in a more convoluted way than they should be.

One such case is when adding settings through the Settings editor in Visual Studio, where only non-generic types seem to be available. If you go there, you’ll notice that all System.Collections classes are available, but no collections from System.Collections.Generic appear as applicable.

Another case is when instantiating types through XAML in WPF. There is no support for passing type arguments in the XML syntax when using a pre-2009 schema. This is made worse by the fact that the 2006 schema seems to be the default for new WPF projects.

1

I think you need to look into some history.

  • C# has been used for a lot longer than it has had generic types for.
  • I expect that the given software had its own StringList at some point in history.
  • This may well have been implemented by wrapping an ArrayList.
  • Then someone a to refactoring to move the code base forward.
  • But did not wish to touch 1001 different files, so finish the job.

Otherwise Theodoros Chatzigiannakis had the best answers, e.g. there are still lots of tools that do not understand generic types. Or maybe even a mix of his answer and history.

2

In some cases it is impossible to use generic types, for example you can’t reference a generic type in XAML. In these cases you can create a non-generic type which inherits from the generic type you originally wanted to use.

If possible, I would avoid it.

Unlike a typedef, you create a new type.
If you use the StringList as an input parameter to a method, your callers can’t pass a List<string>.

Also, you would need to explicitly copy all constructors you whish to use, in the StringList example you couldn’t say new StringList(new[]{"a", "b", "c"}), even though List<T> defines such a constructor.

Edit:

The accepted answer focuses on the pro’s when using the derived types inside your domain model. I aggree that they can potentially be useful in this case, still, I personally would avoid them even there.

But you should (in my opinion) never use them in a public API because you either make your caller’s life harder or waste performance (I also don’t really like implicit conversions in general).

5

For what it’s worth, here’s an example of how inheriting from a generic class is used in Microsoft’s Roslyn compiler, and without even changing the name of the class. (I was so flummoxed by this that I ended up here in my search to see if this was really possible.)

In project CodeAnalysis you can find this definition:

/// <summary>
/// Common base class for C# and VB PE module builder.
/// </summary>
internal abstract class PEModuleBuilder<TCompilation, TSourceModuleSymbol, TAssemblySymbol, TTypeSymbol, TNamedTypeSymbol, TMethodSymbol, TSyntaxNode, TEmbeddedTypesManager, TModuleCompilationState> : CommonPEModuleBuilder, ITokenDeferral
    where TCompilation : Compilation
    where TSourceModuleSymbol : class, IModuleSymbol
    where TAssemblySymbol : class, IAssemblySymbol
    where TTypeSymbol : class
    where TNamedTypeSymbol : class, TTypeSymbol, Cci.INamespaceTypeDefinition
    where TMethodSymbol : class, Cci.IMethodDefinition
    where TSyntaxNode : SyntaxNode
    where TEmbeddedTypesManager : CommonEmbeddedTypesManager
    where TModuleCompilationState : ModuleCompilationState<TNamedTypeSymbol, TMethodSymbol>
{
  ...
}

Then in project CSharpCodeanalysis there’s this definition:

internal abstract class PEModuleBuilder : PEModuleBuilder<CSharpCompilation, SourceModuleSymbol, AssemblySymbol, TypeSymbol, NamedTypeSymbol, MethodSymbol, SyntaxNode, NoPia.EmbeddedTypesManager, ModuleCompilationState>
{
  ...
}

This non-generic PEModuleBuilder class is used extensively in the CSharpCodeanalysis project, and several classes in that project inherit from it, directly or indirectly.

And then in project BasicCodeanalysis there’s this definition:

Partial Friend MustInherit Class PEModuleBuilder
    Inherits PEModuleBuilder(Of VisualBasicCompilation, SourceModuleSymbol, AssemblySymbol, TypeSymbol, NamedTypeSymbol, MethodSymbol, SyntaxNode, NoPia.EmbeddedTypesManager, ModuleCompilationState)

Since we can (hopefully) assume that Roslyn was written by people with extensive knowledge of C# and how it should be used I’m thinking that this is a recommendation of the technique.

1

There are three possible reasons why someone would try to do that off the top of my head:

1) To simplify declarations:

Sure StringList and ListString are about the same length but imagine instead you’re working with a UserCollection that’s actually Dictionary<Tuple<string,Type>, IUserData<Dictionary,MySerializer>> or some other large generic example.

2) To help AOT:

Sometimes you need to use Ahead Of Time compilation not Just In Time Compilation – e.g. developing for iOS. Sometimes the AOT compiler has difficulty figuring out all the generic types ahead of time, and this could be an attempt to provide it with a hint.

3) To add functionality or remove dependency:

Maybe they have specific functionality that they want to implement for StringList (could be hidden in an extension method, not added yet, or historical) that they either can’t add to List<string> without inheriting from it, or that they don’t want to pollute List<string> with.

Alternatively they may wish to change the implementation of StringList down the track, so have just used the class as a marker for now (usually you’d make/use interfaces for this e.g. IList).


I’d also note that you’ve found this code in Irony, which is a language parser – a fairly unusual kind of application. There may be some other specific reason that this was used.

These examples demonstrate that there can be legitimate reasons to write such a declaration – even if it’s not too common. As with anything consider several options and pick which is best for you at the time.


If you have a look at the source code it appears option 3 is the reason – they use this technique to help add functionality/specialize collections:

public class StringList : List<string> {
    public StringList() { }
    public StringList(params string[] args) {
      AddRange(args);
    }
    public override string ToString() {
      return ToString(" ");
    }
    public string ToString(string separator) {
      return Strings.JoinStrings(separator, this);
    }
    //Used in sorting suffixes and prefixes; longer strings must come first in sort order
    public static int LongerFirst(string x, string y) {
      try {//in case any of them is null
        if (x.Length > y.Length) return -1;
      } catch { }
      if (x == y) return 0;
      return 1; 
    }

2

I’d say it is not good practice.

List<string> communicates “a generic list of strings”. Clearly List<string> extension methods apply. Less complexity is required to understand; only List is needed.

StringList communicates “a need for a separate class”. Maybe List<string> extension methods apply (check the actual type implementation for this). More complexity is needed to understand the code; List and the derived class implementation knowledge is required.

7

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật