2015-05-12 4 views
3

У меня есть Address класс:Почему этот общий метод не совпадает?

public class Address 
{ 
    //Some stuff 
} 

и есть соответствующий *Wrapper класс для обеспечения соблюдения определенных правил о том, как использовать Address класс:

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

    public Address GetWrapped() 
    { 
     return _wrapped; 
    } 

    //And some more 
} 

где IWrapped определяется как:

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

У меня есть следующее поколение IC класса для сохранения этих объектов (есть и другие объекты, которые следуют этой модели Entity и 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 
    } 
} 

и у меня есть тестовый код:

[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 имеет тип IWrapped<Address> (т.е. , он ее реализует) и Address - это параметр типа, присвоенный методу AddList, поэтому типы должны выстраиваться в линию. Я знаю, что это связано с моим ограниченным знанием C# дженериков (знакомых с генериками Java), но не могу понять, что здесь не так --- должно работа.

Это, вероятно, не имеет никакого значения, но вот мой конфиг:

  • NHibernate 4.x
  • .NET Framework (4,5)
+1

Я отредактировал ваше название. Пожалуйста, смотрите: «Если вопросы включают« теги »в их названиях?] (Http://meta.stackexchange.com/questions/19190/), где консенсус« нет, они не должны ». –

+0

@JohnSaunders Спасибо за редактирование и ссылку. – markvgti

+0

[DotNetFiddle] (https://dotnetfiddle.net/farucU), показывающий проблему. – Sayse

ответ

0

NB: См. Ответ от @StefanSteinegger, это особенно интересно.

То, что сработало для меня меняется так, как я был определяющий addrList, от:

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

к:

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

Однако, я также изменение сигнатуры метода GenericRepository.AddList<T>(..) взять IEnumerable, что также помогает указывать, что вход доступен только для чтения. Итак:

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

Это из-за отсутствующего типа дисперсия IList<T>. (IList<int> не является IList<object>).

Использование IEnumerable<T>, потому что ковариантен:

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

Причина: Если вы получаете экземпляр List<AddressWrapper>, компилятор не знает, если он совместим с любой возможно осуществление IList<IWrapped<T>>. Предположим, что еще один класс, который реализует IWrapped<T>. Он не будет совместим при записи в Список. Даже если вы не записываете в список в AddList, компилятор принимает только совместимые типы. IEnumerable<T> не может быть написано, поэтому он может быть вариантом.

Не относится к вопросу, который я предлагаю использовать ковариации для собственного интерфейса, а также:

public interface IWrapped<out T> 

сделать IWrapped<Thing> совместимый с IWrapped<SpecificThing>.

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

+0

Спасибо за этот ответ: это помогло мне понять больше, чем только мою конкретную проблему. – markvgti

0

В точке, в которой вы пытаетесь вызоваAddList(), для всех компилятор знает, что метод может добавлять объекты любого типа, который реализует IWrapper<Address> (то есть типы, которые Арен 't AddressWrapper) к отобранному списку.

Это было бы плохо, потому что список, который вы пытаетесь передать методу, не хочет содержать ничего, кроме AddressWrapper.

1

Чтобы это было ясно на примере. Не могли бы вы ожидать, если бы у нас было два типа: 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(); 
    } 
} 

И мы старались, чтобы добавить их в третий список внутри AddList<T>:

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

Тип системы держит вас от ошибки. List<T> не является ковариантным именно по этой причине.

Смежные вопросы