views:

58

answers:

4

There must be something fundamental about interfaces/generics I have not yet learned. I hope to learn it now.

Here is the scenario:

I have this interface and class:

public interface IInterface
{
    string TestValue { get; set; }
}

public class RealValue: IInterface
{
    public string TestValue { get; set; }
}

If I create a method like this it compiles just fine:

public class RandomTest: IMethodInterface
{
   public IInterface GetRealValue()
   {
       RealValue realValue = new RealValue();
       return realValue;
   }
 }

Note that I am returning an object that implements the interface.

Now, if I add to the RandomTest class a method that returns list then it does not work anymore:

 public List<IInterface> GetRealValues()
 {
    List<RealValue> realValues = new List<RealValue>();
    return realValues;  // ERROR Here <- says it can't convert to a List<IInterface>
 }

So, my guess is that generics can't figure this out, but why?

Is there a way around this? What do you do when the return value of the method above is locked because you are implementing an interface like this:

public interface IMethodInterface
{
    IInterface GetRealValue();
    List<IInterface> GetRealValues(); // Can't just convert the return types to a concrete 
                                      // class because I am implementing this.  This 
                                      // interface is in a separate project that does not 
                                      // have the concrete classes.
}

Is there any hope? What would you do?

+1  A: 

A List<RealValue> cannot be used in place of a List<IInterface>. If it was permitted, the caller would be able to Add an IInterface to the returned list that is of a type other than RealValue.

Marcelo Cantos
+1  A: 

Note that if you could convert List<RealValue> to List<IInterface> you could call .Add(anyObjectImplementingIInterface) which cannot work.

You can, however, use .Cast<IInterface>().ToList().

Philipp
+1  A: 

The reason for this is that List<RealValue> is a specific type, which does not inherit List<IInterface>, so it cannot be converted.

However, in .NET 4.0 you're in luck. The interface IEnumerable<out T> specifies that T can be the class, or a base class, so you can change your method to:

IEnumerable<IInterface> GetRealValues();

on .NET 4.0. Note that this only works because IEnumerable has the out keyword specified on the template parameter.

The out keyword means two things:

  1. The type before which you put the out keyword can only be used for types that go out of the class. So, public T MyMethod() is allowed, but public void MyMethod(T myParam) is not allowed, because this goes into the class;

  2. Because of this restriction, .NET knows that T can be cased to everything that inherits from T. Because of the restriction, this is guaranteed to be a safe operation.

Pieter
This looks nice. I will have to dig more to understand what `out` means for a generic type param. Either way, I tried it and it worked.
Vaccano
Expanded the answer with a little bit more info concerning the `out` keyword.
Pieter
Sweet! Thanks for the expanded answer!
Vaccano
A: 

your return type is List<IInterface> and you want return List<RealValue> but List<RealValue> does not have any implementation for List<IInterface> to do casting so you going to got a compile error, but you can do this to resolve a problem:

List<RealValue> currentVal = ...;
return currentVal.Cast<IInterface>().ToList();
SaeedAlg