A Generic Constraint Question

A Generic Constraint Question

Rate This
  • Comments 23

Here's a question I get fairly frequently: the user desires to create a generic type Bar<T> constrained such that T is guaranteed to be a Foo<U>, for some U.

The way they usually try to write this is

class Bar<T>  where T : Foo<T> {...

which of course then does not work. The user discovers this when they type

 new Bar<Foo<string>>()

and get an error stating that the constraint has been violated. Which it certainly has. The constraint says that T must be Foo<T>, so therefore string must be Foo<string>, which clearly it is not.

There are a few ways to solve this problem. One is to simply introduce a new type parameter:

 class Bar<T, U> where T : Foo<U>

this means that you now have some redundancy:

new Bar<Foo<string>, string>()

But let's stop and think for a moment about what the user desired in the first place. Suppose there were a way to specify what we sometimes call a "mumble type" on the C# design team:

class Bar<T>  where T : Foo<?> {...

Think about the consequences of that on the caller side; the compiler could verify that new Bar<Foo<string>>() was legal. But what could the compiler verify on the callee side?

class Bar<T>  where T : Foo<?> {
   public void Blah(T t) {
    t.

t dot what? We don't know anything about T except that it is Foo<something>. What methods, properties, fields or events of Foo could we access? Only those that in no way consume the generic type, which seems like precious little. If Foo had a whole lot of stuff that wasn't generic, why was it made generic in the first place?

But this then leads to an insight; if the user owns the Foo<T> class, then they could do precisely that:

public abstract class FooBase
{
  private FooBase() {} // Not inheritable by anyone else
  public class Foo<U> : FooBase {...generic stuff ...}
  ... nongeneric stuff ...
}

public class Bar<T> where T: FooBase { ... }
...
new Bar<FooBase.Foo<string>>()

and now everyone is happy. The compiler restricts the construction of Bar to FooBase on the caller side. The compiler ensures that every runtime instance of FooBase is an instance of Foo<T>. And the compiler allows the callee side to use all the non-generic methods on FooBase.

Personally, I would go for the first solution myself. But it's edifying to try and find additional solutions, even if they are a bit odd.

  • I like where this is going:

    Ignoring the typing issue (the meat of you example) if the type U in the generic Foo<>, what if I could instead say?

      class Bar<T> where T : Foo<U> where U : new

    or

      class Bar<T> where T : (Foo<U> : where U : new)

  • I think I'm missing why anyone would want the original interface. Why not implement it like this (in C++, but I'm sure the translation is straightforward):

    template<typename U>

    class Bar {

     typedef Foo<U> T;

     // ...

    };

    (of course, in C++, you could only declare the base template, then specialize for Bar<Foo<U>> to get the desired effect, but the upshot would be the same -- you'd get U as a template parameter instead of T)

  • Minor, but I think that should be 'Foo<U> : FooBase' in stead of 'Foo<U> : Foo'.

    I'd also use the first solution, as it immediately, without indirection, expresses the programmer's intention. A bit of redundancy is not so bad compared to needing how exactly Foo and FooBase relate.

    Wouldn't it be possible to extend type inference a bit to allow you to specify only Foo<string> and have the compiler infer U = string by itself? You would of course need syntax to specify which types you're trying to have inferred; that might be something like 'new Bar<Foo<string>,>' (note the comma).

  • That was a typo. Fixed. Thanks!

    And yes, we have considered adding "mumble types" to the language in a variety of contexts; this would be a possible context in which "new Bar<Foo<string>, ?>" might make sense.  However, we have no plans to add them to the language any time soon.

  • Unless I'm mistaken, it is possible to do this in Java right now with their mumble type declarations:

    class Bar<T extends Foo<? extends U>> { ... }

    You could use any generic methods of Foo as though the type parameter was U, even if the actual type parameter is some subclass of U.

  • A slightly related, but still unrelated question: Why are there no typedefs in C#. Sometimes it would be useful to  say something like

    class X<T> {

     class Y<T> {

     }

     internal typedef Dictionary<T, IEnumerable<Y<T>>> MySensibleName;

    }

    I realize public and protected typedefs would be hard since all callers would need to be recompiled if the typedef was changed, but what would be the problem with internal and private ones?

  • I hate Java's generics with a passion (type-erasure is horrible) but I really do like the wildcard type constraint stuff as Andrew Cook demonstrated.  I also like the call-site variance.

    E

    rik: you can use the using statement to alias things.

    using Dictionary<T, IEnumerable<Y<T>>>  = MySensibleName

    It is unfortunate that it uses the same using keyword.

  • Andrew Cook: Actually, what you are describing there is how Java does contravariance/covariance. See my blog archive on this subject for how we are considering doing this in a future version of C#.

    Mark: Call site variance is interesting, but kind of strange. We are probably not going to support it in C#.

    Mark, Erik:  You can use "using" as a form of typedef, but not in a generic manner.  That is, you cannot say:

    using Frib.Frab.Blah.Abc.Def<T> = MyDef<T>

    to define a "family" of typedefs. Though I agree that it would be useful, unfortunately this did not make the short list of features that we are going to do for the next version of C#.

  • I do agree that using is possible, but it only works in one file. Sometimes in those cases, I derive a new class

    internal class MySensibleName : Dictionary<T, IEnumerable<Y<T>> {}

    Does anyone have a better idea?

  • Generally when I've found myself in the situation of having a generic type for implementation reasons but callers aren't interested in the type parameter(s), I use a non-generic or less generic interface rather than base class to abstract away the type parameter(s) the caller doesn't care about, so IFoo instead of FooBase.

    As for mumble types and typedefs, I find where I want the abbreviation is not in defining the generic type but in using it. So for example if I have T<U,V> where U : IComparer<V>, I hate having to write new T<MyStringComparer, string>. It's like preschool programming: the compiler says "So we know that MyStringComparer implements IComparer<string>, so that means V must be.......?" and we have to shout out "string!" and the compiler says "Very good! Now let's all write string right there. Has everybody done that now?"

    I would rather be able to write something like T<U, infer V> where U : IComparer<V> or to have public interface IComparer<T> { typedef T ComparandType; ... } and then T<U, ?> where U : IComparer<?> and then refer to ? as U.ComparandType. In either case, even though the CLR would still see T`2, the compiler would apply the syntactic sugar when I write T<MyStringComparer> and emit T`2[MyStringComparer, System.String].

  • I find I am frequently defining empty classes and interfaces to give a name to the most frequently used specializations of generic types. This is where I would love a public typedef. It would just be a little metadata that creates a public alias. To the consuming assembly it would look just like a type and the runtime would treat it the same way the C++ compiler treats typedefs.

  • @MKane,

    I also would like to be able to do away with "preschool programming". In the business layer of one of my applications I have a large set of classes with two generic parameters where one of the types is inferable but the compiler requires it to be written out anyway.

    I also would like to see at least internal typedefs. I don't need them all that often, but I do find myself missing them occasionally.

  • Eric Lippert vient de publier un excellent post sur les génériques . L'idée est de pouvoir faire ceci

  • > Generally when I've found myself in the situation of having a generic type for implementation reasons but callers aren't interested in the type parameter(s), I use a non-generic or less generic interface rather than base class to abstract away the type parameter(s) the caller doesn't care about, so IFoo instead of FooBase.

    One thing that used to come up frequently on the newsgroup (when I still had time to read) was people trying to find some declaration which captured the spirit of

    void Baz(List<anything> list)

    We would lead them socratically to the realisation that the type that expressed the idea 'what behaviour is common to all List<anything>s' best is IList.

  • You have typedef in C# it's called 'type alias' :

    try this:

    using System;

    using IntList = System.Collections.Generic.List<int>;

    class Program

    {

    static void Main(string[] args)

    {

           IntList list = new IntList();

           list.Add(3);

           Console.WriteLine(list[0]);

    }

    }

Page 1 of 2 (23 items) 12