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)
source to share
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
}
source to share
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
source to share
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.
source to share
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.
source to share