Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this generic method call not match?

Tags:

c#

generics

I have an Address class:

public class Address
{
    //Some stuff
}

and there's a corresponding *Wrapper class to enforce certain rules on how to use the Address class:

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

    public Address GetWrapped()
    {
        return _wrapped;
    }

    //And some more
}

where IWrapped is defined as:

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

I have the following generic class for saving these entities (there are other entities that follow this pattern of 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 is of type IWrapped<Address> (i.e., it implements it) and Address is the type parameter being given to the AddList method, so the types should line up. I know that 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.

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

  • NHibernate 4.x
  • .NET Framework (4.5)
like image 253
markvgti Avatar asked Mar 21 '26 09:03

markvgti


1 Answers

This is because of the missing type variance of IList<T>. (IList<int> is not an IList<object>).

Use IEnumerable<T>, because it is covariant:

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

Reason: If you get an instance of List<AddressWrapper>, the compiler doesn't know if it is compatible with any possible implementation of IList<IWrapped<T>>. Assume another class that implements IWrapped<T>. It wouldn't be compatible when writing to the List. Even though you don't write to the list in AddList, the compiler only accepts compatible types. IEnumerable<T> cannot be written, so it can be variant.

Not related to the question I suggest to use covariance for your own interface as well:

public interface IWrapped<out T>

to make IWrapped<Thing> compatible with IWrapped<SpecificThing>.

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

like image 179
Stefan Steinegger Avatar answered Mar 23 '26 23:03

Stefan Steinegger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!