Why doesn't this general method match?

I have a class Address

:

public class Address
{
    //Some stuff
}

      

and there is a corresponding class *Wrapper

to enforce certain rules on how to use the class Address

:

public class AddressWrapper : IWrapped<Address>
{
    private Address _wrapped;

    public Address GetWrapped()
    {
        return _wrapped;
    }

    //And some more
}

      

where is IWrapped

defined as:

public interface IWrapped<T>
{
    T GetWrapped();
}

      

I have the following generic class for storing these objects (there are other objects that follow this pattern Entity

and EntityWrapper

):

public class GenericRepository
{
    private GenericRepository() { }

    public static void Add<T>(IWrapped<T> entity)
    {
        //Do something
    }

    public static void AddList<T>(IList<IWrapped<T>> entities)
    {
        //Do something
    }
}

      

and I have this test code:

[Test]
public void UseGenericRepository()
{
    AddressWrapper addrW = new AddressWrapper();
    addrW.AddrLine1 = "x";
    addrW.AddrLine2 = "y";
    addrW.AddrLine3 = "z";
    addrW.City = "Starling City";
    //This works as expected
    GenericRepository.Add<Address>(addrW);

    IList<AddressWrapper> addrList = new List<AddressWrapper>();
    //Fill up the addrList

    //This gives error: best overloaded method match has some invalid
    //arguments
    GenericRepository.AddList<Address>(addrList);
}

      

AddressWrapped

has a type IWrapped<Address>

(i.e. implements it) and Address

is a type parameter provided to the method AddList

, so the types must match. I know this is due to my limited knowledge of C # generics (familiar with Java generics) but can't figure out what's wrong here --- it should work.

It probably doesn't make any difference, but here's my config:

  • NHibernate 4.x
  • .NET Framework (4.5)
+3


source to share


4 answers


NB: Please see the answer from @StefanSteinegger, this is especially interesting.

What worked for me was changing the way I was defining addrList

from:

IList<AddressWrapper> addrList = new List<AddressWrapper>();

      

in



IList<IWrapped<Address>> addrList = new List<IWrapped<Address>>();

      

However, I also change the method signature GenericRepository.AddList<T>(..)

to use IEnumerable

, as this also indicates that the input file is read-only. So:

public static void AddList<T>(IEnumerable<IWrapped<T>> entities)
{
    //Do some stuff
}

      

0


source


This is due to the lack of type variance IList<T>

. (is IList<int>

not IList<object>

).

Use IEnumerable<T>

because it is covariant:

public static void AddList<T>(IEnumerable<IWrapped<T>> entities)
{
    //Do something
}

      

Cause. If you get an instance List<AddressWrapper>

, the compiler doesn't know if any possible implementation is compatible IList<IWrapped<T>>

. Let's assume another class that implements IWrapped<T>

. It will not be compatible when written to the List. Even if you don't write the list in AddList

, the compiler only accepts compatible types. IEnumerable<T>

cannot be written, so it might be an option.



Unrelated to the question, I suggest using covariance for your own interface as well:

public interface IWrapped<out T>

      

make it IWrapped<Thing>

compatible with IWrapped<SpecificThing>

.

MSDN: https://msdn.microsoft.com/en-us/library/ee207183.aspx

+5


source


To make it clear with an example. Would you like to expect if we had two types IWrapped<T>

:?

public class AddressWrapper : IWrapped<Address>
{
    private Address _wrapped;

    public Address GetWrapped()
    {
        return _wrapped;
    }

    //And some more
}

public class OtherWrapper : IWrapped<MailBox>
{
    public MailBox GetWrapped()
    {
        throw new MailBox();
    }
}

      

And we tried to add them to the third list inside AddList<T>

:

public static void AddList<T>(IList<IWrapped<T>> entities)
{
   internalList = new List<IWrapped<T>>(); 
   list.AddRange(entities); // BOOM.
}

      

The type system prevents you from making mistakes. List<T>

is not covariant for this very reason.

+1


source


The moment you try to call AddList()

, for all compilers, this method can add objects of any type that implements IWrapper<Address>

(i.e. types that are not AddressWrapper

) to the list.

This would be bad, because the list you are trying to pass to the method doesn't want to contain anything other than AddressWrapper

s.

0


source







All Articles