Generics, interfaces and casting
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 4 of 4

Thread: Generics, interfaces and casting

  1. #1
    Join Date
    Jul 2002
    Location
    EU
    Posts
    68

    Generics, interfaces and casting

    I hope this is the right forum for my question.

    I am trying to write a generic class parameterized by an interface, and to down-cast an instance, but I always get an error. More exactly, I have the following:

    Code:
    interface IWhatever {... }
    
    class IWhateverImpl : IWhatever { ... }
    
    class CGeneric<T> where T:IWhatever { ... }
    
    class Program {
    
        CGeneric<IWhatever> mGeneric;
    
        Program()
        {
             mGeneric = new CGeneric<IWhateverImpl>();
        }
    ...
    }
    The typecast in line:
    Code:
     mGeneric = new CGeneric<IWhateverImpl>();
    throws an exception.

    Somehow I did not manage to google up this problem. Has anyone read anything about it, or has any idea about a solution / workaround to such a problem?

  2. #2
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Generics, interfaces and casting

    Up until C# 4.0, any type casting of the given kind was forbidden by the language for generic types (the generic types were invariant) - although you could do that with arrays.

    Anyway, C# 4.0 introduced some more flexibility, but there are certain restrictions.
    Now you can define generic interface parameters (but not generic class parameters) as covariant or contravariant, which will enable you to do certain casts.
    Now, I know all this sounds a bit too "technical", but it is really simple in essence.

    This excerpt from a blog article should clarify it a bit:
    Consider this simple example [... using ...] C# 4.0 syntax:
    Code:
    // Covariant parameters can be used as result types
    interface IEnumerator<out T> {
          T Current { get; }     // [Note that an object of type T is the result of the getter...]
          bool MoveNext();
    }
    
    // Covariant parameters can be used in covariant result types
    interface IEnumerable<out T> {
          IEnumerator<T> GetEnumerator();      // [Note that GetEnumerator() results in IEnumerator<T>]
    }
    
    // Contravariant parameters can be used as argument types
    interface IComparer<in T> { 
          bool Compare(T x, T y);      // [Note that the method accepts arguments of type T]
    }
    This would mean we could write code like the following:
    Code:
     
          IEnumerable<string> stringCollection = ...;
          IEnumerable<object> objectCollection = stringCollection;
          foreach( object o in objectCollection ) { ... }
     
          IComparer<object> objectComparer = ...;
          IComparer<string> stringComparer = objectComparer;
          bool b = stringComparer.Compare( "x", "y" );
    But we couldn’t do the opposite – try and convert an IEnumerable<object> to an IEnumerable<string>, or try and convert an IComparer<string> to an IComparer<object>. In those cases, we’d get a compile time error telling us such a conversion wouldn’t be type-safe.
    That article also provides some explanations why type variance should be restricted, so make sure to read it (the link is above). Note that the article is written before C# 4.0 was released, but was updated later, so don't get confused by some sentences.

    Also, here's what MSDN documentation says about it:
    Covariance and contravariance are collectively referred to as variance. A generic type parameter that is not marked covariant or contravariant is referred to as invariant. A brief summary of facts about variance in the common language runtime:
    • In the .NET Framework version 4, variant type parameters are restricted to generic interface and generic delegate types.
    • A generic interface or generic delegate type can have both covariant and contravariant type parameters.
    • Variance applies only to reference types; if you specify a value type for a variant type parameter, that type parameter is invariant for the resulting constructed type.
    • Variance does not apply to delegate combination. That is, given two delegates of types Action<Derived> and Action<Base> (Action(Of Derived) and Action(Of Base) in Visual Basic), you cannot combine the second delegate with the first although the result would be type safe. Variance allows the second delegate to be assigned to a variable of type Action<Derived>, but delegates can combine only if their types match exactly.

  3. #3
    Join Date
    Jun 2008
    Posts
    2,477

    Re: Generics, interfaces and casting

    First off, I would ask why you think you need to perform that cast at all. The entire point of using an interface is to avoid casting. YOu can work with objects through their interface opaquely without any knowledge of the underlying type. You are receiving an error because you have declared mGeneric as a CGeneric<IWhatever>. CGeneric is not polymorphic in that regard and you cannot later assign an instance of CGeneric<Whatever> to it even if "Whatever" implements the interface (btw, this is called "covariance" and is supported to some degree in the latest version of C#. I still work with an older version of C# at work and am not an expert on the subject, so maybe someone else can explain these limitations in greater detail for you).

    EDIT: Ninja'd
    Last edited by BigEd781; November 26th, 2010 at 11:39 AM.

  4. #4
    Join Date
    Jul 2002
    Location
    EU
    Posts
    68

    Re: Generics, interfaces and casting

    Thank you very much for your responses! Indeed, I am a total beginner in C# generics (would I ever be otherwise?...), and the impossibility of doing what I thought was right irked me.

    TheGreatCthulhu: Thanks a lot for the info, it seems to be exactly what I need to study.

    BigEd781: the point of my "wish" is to expose mGeneric as CGeneric<IWhatever>. Internally, mGeneric should be initialized as a CGeneric<IWhateverImpl>; externally, it should be seen only as CGeneric<IWhatever>. Its external consumers should thus be de-coupled from any internal implementation of IWhatever. (I realize now that I chose poorly the name of IWhateverImpl: that is actually a class, which implements interface IWhatever. I should have used something like "ImplementationOfIWhatever".)
    Last edited by petru66; November 29th, 2010 at 05:36 AM.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center