In my current project I have come across the requirement to create generic classes with the same name, but different numbers of generic parameters. For example:

MyClass<T1, T2>
MyClass<T1, T2, T3>

Given that I want all of these in the same namespace, I am confused as to how to structure and name my classes and files?

If we follow the idea that we should have classes limited to one per file and that files should be in a folder structure that represents namespace hierarchy and that the name of the file should match the name of the class, how do I deal with this situation?

What I am really asking here is what should I name the file that contains MyClass<T1>, and what should I name the file that contains MyClass<T1, T2>? I am not asking what the names of the type parameters should be.

  • Give us some specific examples that describe the problem in better detail. The examples you've provided are too... erm, generic. What do you mean by "how to structure and name my classes and files?" – Robert Harvey Jan 16 '15 at 0:07
  • Microsoft does this itself by just appending a number to the type parameter. See the Tuple docs: msdn.microsoft.com/en-us/library/… – Pete Jan 16 '15 at 0:09
  • @Pete: That really only applies to Tuple. Microsoft also uses the TKey, TValue convention. Func has a TResult type parameter. Though I do agree that you can use T1, T2, etc. for a variable number of input parameters that don't otherwise have specific uses like TKey and TValue. – Robert Harvey Jan 16 '15 at 0:11
  • @RobertHarvey Well, yes but only in the context of an actual key/value collection such as a dictionary. For anything consisting of a variable number of types they append a number. Here's another example: msdn.microsoft.com/en-us/library/dd402872(v=vs.110).aspx – Pete Jan 16 '15 at 0:14
  • 1
    Well, your edits obsolete some of the comments. :) Why can't you just keep the classes in the same physical file? If they're so different that you need to keep them in separate files then can you tell us what makes them different? – Pete Jan 16 '15 at 0:31

And so on, where the number after the backtick is the number of generic type parameters. This convention is used by Microsoft.

Alternatively, you can use something like

MyGenericCollectionClass[TKey, TValue].cs

which preserves not only the number of generic type parameters, but also their specific names. Granted, it doesn't preserve the angle brackets, but we can't have everything in life we want, can we?

  • 1
    The compiler internally mangles the name of generic types, adding a backtick and the number of generic parameters since .NET does not allow multiple types with the same name with a differing number of generic parameters but C# does. So the file naming convention matches what the compiler does. – CodesInChaos Jan 16 '15 at 9:03
  • 1
    I wanted to prove you wrong, but actually you are correct: github.com/dotnet/corefx/tree/master/src/… I don't like this convention. – Den Jan 16 '15 at 9:25
  • 1
    @Den, it seems that now they have regreted from that convention. Maybe it implied some problem? github.com/dotnet/corefx/commit/… – Sam Aug 29 '16 at 15:49
  • @Sam Hopefully, not a communication problem between Microsoft teams. Because nobody else is doing it. Also it's up for the compiler team, not library team to decide in my opinion. – Den Aug 30 '16 at 8:45
  • I'd not have the balls for using " ` " in a file name. – Cristian E. Oct 2 '17 at 10:35

In the case of Tuple and Action that Pete has mentioned, Microsoft themselves use a single file - see Tuple.cs and Action.cs.

I think it partly depends on whether or not the functionality for all the classes is basically the same. Personally I dislike lumping classes into a single file, but this might be an exception. In the source code where I work I added an autogenerated (using T4) NamedTuple class which acts in the same way as Tuple, but with a string name as the first argument into the constructor.

To answer your question, if you don't want to use a single file, perhaps use MyClass_1.cs for MyClass<T1>, MyClass_2.cs for MyClass<T1, T2>, etc.

Neither option is ideal though, so I'd be inclined to suggest the "Microsoft do it this way, so..." argument.

  • 2
    Tuple and Action are all delegates; they don't have any implementation code, so putting all of the variations in separate files would be kinda pointless anyway. Just proves that every rule has an exception. – Robert Harvey Jan 16 '15 at 1:03
  • Yeah generally speaking putting multiple public delegates into the same file is considered ok (provided they're related). – Stephen Jan 16 '15 at 1:04
  • 1
    @RobertHarvey, Tuple is not a delegate, but each implementation is short anyway. – Arturo Torres Sánchez Jan 23 '15 at 17:42
  • @ArturoTorresSánchez: Right, I was thinking of Func. – Robert Harvey Jan 23 '15 at 17:45

Have you asked yourself, if the classes do really have the same intent? If one class is more generic than the other, then it will be a GenericClass, a MoreGenericClass and a MostGenericClass. Imagine that each type parameter of a class adds a new dimension to the class, so it may help to ask what dimension it is.

Lets take this example:

  • Container<Thing>
  • MetricContainer<Thing, Metric>
  • MetricTransportableContainer<Thing, Metric, Transport>

I'm aware, that it is not the best example, but very expressive to show three dimensions:

  • inner Dimension, what it can load
  • metric Dimension, with which metric it can be loaded, only by count of things or by square measure or by cubical capacity or by weight
  • outer Dimension, where it can be loaded.

So you can model transportation of cars:

MetricContainer<Cars, CountMetric>
MetricTransportableContainer<Cars, CountMetric, Ship>

Transportation of fluids:

MetricContainer<Fluid, Volume>
MetricTransportableContainer<Fluid, Volume, Shelf>

Transportation of energy:

MetricContainer<Energy, ElectricPower>
MetricTransportableContainer<Energy, ElectricPower, Box>

Transportation of sugar, cereals:

MetricContainer<CrumblyMaterial, Weight>
MetricTransportableContainer<CrumblyMaterial, Weight, Silo>

Oh, what a surprise: a List<T> has one dimension that represents the things the list can hold; and this is a Map<T, S> with two dimensions that represent the things the map can hold and the access keys.

  • I don't think you understood the question. Look at the last paragraph of the question. – Robert Harvey Jan 16 '15 at 14:12
  • @RobertHarvey Please read the question carefully: classname should be aquivalent to filename. It's interchangeably. The problem lies in wrong analysis of the class and wrong naming of the generic classes in general. To sum up my answer: "Name it what it is and not what it seems to be." – shylynx Jan 16 '15 at 14:34
  • @RobertHarvey You are kiding me ;-) Put Container<Thing> into file Container.cs. Put MetricContainer<Thing, Metric> into file MetricContainer.cs and put MetricTransportableContainer<Thing, Metric, Transport> into MetricTransportableContainer.cs. Please read my last sentence carefully than you will understand that the question handles three completely different objects. – shylynx Jan 16 '15 at 14:54
  • @RobertHarvey Read my answer: Put one class into one file! – shylynx Jan 16 '15 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.