Generics limitation or flaw?

December 8th, 2007

I'm getting some difficulties while working with .NET generics. For example. the following code:

C#:
  1. class Zen { int Id; }
  2. ...
  3. public void DoSomething<t>( T t )
  4. {
  5.     t.Id = 0;
  6. }
  7. ...
  8. DoSomething<zen>( new Zen() );

The DoSomething code won't compile, because the compiler can't guess that the typed parameter T has the Id property. We could arrange it like this:

C#:
  1. public void DoSomething<t>( T t )
  2.     where T :  Zen
  3. {
  4.     t.Id = 0;
  5. }

And now it compiles. What I don't know is: why can't the compiler infer the current type from the invocation and acknowledge that in fact the given parameter type(Zen) has the Id property. I don't really get the where keyword's utility, because I don't see the difference from the previous excerpt to this one:

C#:
  1. public void DoSomething(Zen z )
  2. {
  3.     z.Id = 0;
  4. }

Isn't this a major generics limitation? Or is it just me?

Related Posts

8 Responses to “Generics limitation or flaw?”

  1. Hugo Batista Says:

    hi

    guess you’re confusing the time when specialized generic type is created. It’s not by the compiler, but by the runtime:
    http://msdn2.microsoft.com/en-us/library/f4a6ta2h.aspx

    if it wasn’t like this, thing on MakeGenericMethod, for example. It would be really different to implement, with runtime assembly generation…

    the where constraint will tell the compiler how to type check what’s happening (because c# is a type check language). In VB, your code is actually valid…. :)

    my 2 cents
    cheers

  2. Hugo Batista Says:

    I meant “think on MakeGenericMethod” :)

  3. Nelson Correia Says:

    In contrast with C++/Java templates/generics, C# generics are not only syntatic sugar. They exist at runtime (like Hugo Batista said) and the runtime need to know what to type check.

    In your first code sample, the runtime would raise an exception (if this code compiled), if type T doesn’t have an Id property.

    That would be strange: the runtime trying to call a method (get_Id) on a type that doesn’t have it! Where is type check?!

    Your second doubt (”I don’t see the difference from the previous excerpt to this one”) made me think, and it really looks very similar. But imagine that instead of Zen you have IZen (the Zen interface), and you want to create an IZen object inside the DoSomething method.

    If this method is not generic and returns an IZen, there is no way you could execute “return new IZen();”. But instead, if this method is generic, with at least two constraints (”where T : IZen” and “where T : new()”), you could make “return new T();”. :)

  4. Pedro Santos Says:

    Yes, I know that generics are checked at runtime, but the MakeGenericMethod is used on reflexion, is natural to check everything when using reflexion, and that’s why reflexion is slower.

    For exemple:
    List list = new List();
    list.Add(”zen”);
    list[0].Trim();

    Well, how come the compiler can infer the typed parameters object while accessing, but don’t allow the same behavior on a method:

    void Buu( T a ) { a.ToLower(); }
    Buu(”asdasd”);

  5. ilitirit Says:

    Type-safety is fundamental to the CLI. Constraints are required by the compiler so that it knows how to generate the IL. That way the type-checking can be performed at compiler and runtime level when linking to external DLL’s without requiring the method definition.

    C++ templates are limited in that sense because in order for clients to make use of generic classes or methods in a library, the template definitions have to be made available.

    http://msdn2.microsoft.com/en-us/library/ms379564(VS.80).aspx#csharp_generics_topic4

  6. Nelson Correia Says:

    Well, I don’t know if I understood your last example, but the first code sample does not compile unless you do “((string)list[0]).Trim()”, because Object doesn’t have a Trim method.

    As well, the second code sample doesn’t compile either, because T doesn’t (couldn’t) have a ToLower method.

  7. Pedro Santos Says:

    Nelson, the blog engine removed the tempalte arguments, it was List[string]. :)

    Yes, T doesn’t have ToLower, but for the invocation of Buu[string] the compiler knows that in that case, T has ToLower. :) It´s like knowing in the lsit example that T has the Trim()

    Nelson, I’ve seen you blog, and I know you from ISEL. Congrats on finishing LEIC!

  8. Nelson Correia Says:

    First of all, thank you! :)

    Back to the code:
    1. void Buu[T]( T a ) { a.ToLower(); }
    2. Buu[string](”asdasd”);

    Even that this code compiled, the compiler must have checked the entire code, searching for all the calls to Buu[T] and assuring that all types parameterizing it have a ToLower method (or whatever method or field the method body uses).

    Aditionaly, this code had to be private! If it wasn’t, some programmer could reference this assembly, passing a xpto to Buu[T], which doesn’t have a ToLower method, raising an exception at runtime. A little tricky :)

    (Keep in mind that when you call a method, the compiler checks the signature of that method, but not the entire method body)

    In the List[T] example, we create an object, and the compiler works with that object, having all the information associated with it, notably the type parameter. This makes the object’s “signature”.