2016-02-26 2 views
4

В моем проекте у меня есть структура следующего класса:Почему этот метод расширения работает с дженериками, но не с базовым типом набора?

public interface IUpdateableModel 
{ 
    ModelState State { get; set; } 
    void ResetState(); 
} 

public abstract class UpdateableModel : IUpdateableModel 
{ 
    public ModelState State { get; set; } 
    public void ResetState() 
    { 
     //Perform reset logic 
    } 
} 

public class MyUpdateableClass : UpdateableModel 
{ 
    //Some properties. 
} 

Теперь я пытаюсь добавить некоторые методы расширения для использования с коллекциями IUpdateable:

public static class UpdateableModelExtensions 
{ 
    public static bool HasUnsavedChanges(this IList<IUpdateableModel> collection) 
    { 
     return collection.Any(x => x.State != ModelState.Unmodified); 
    } 

    public static void ResetItemStates<T>(this IList<T> collection) where T : IUpdateableModel 
    { 
     var itemsToRemove = collection.Where(x => x.State == ModelState.New).ToList(); 
     foreach (var item in itemsToRemove) 
     { 
      collection.Remove(item); 
     } 

     var itemsToAdd = collection.Where(x => x.State == ModelState.Deleted).ToList(); 
     foreach (var item in itemsToAdd) 
     { 
      item.State = ModelState.Unmodified; 
     } 

     var itemsToReset = collection.Where(x => x.State == ModelState.Modified).ToList(); 
     foreach (var item in itemsToReset) 
     { 
      item.ResetState(); 
     } 
    } 
} 

Как написано при использовании этого на List<MyUpdateableClass> возникает ошибка компилятора, что типы не совпадают.

public class MyClass 
{ 
    public IList<MyUpdateableClass> Items {get; set;} 

    public void MyMethod() 
    { 
     if(Items.HasUnsavedChanges()) //Compiler error 
     { 
      //Do some stuff 
     } 
    } 
} 

Ошибка компилятора:

'IList<MyUpdateableModel>' does not contain a definition for 
'HasUnsavedChanges' and the best extension method overload 
'UpdateableModelExtensions.HasUnsavedChanges(IList<IUpdateableModel>)' 
requires a receiver of type 'IList<IUpdateableModel>' 

Тот же результат виден, если метод расширения изменяется на IList<UpdateableModel>

Однако, если я вместо этого использовать дженерики осуществить это, он отлично работает:

public static bool HasUnsavedChanged<T>(this IList<T> collection) 
where T : IUpdateableModel 
    { 
     return collection.Any(x => x.State != ModelState.Unmodified); 
    } 

Также, если я поменяю использование на Items.Cast<IUpdateableModel>().ToList() первая версия делает работа.

Каковы технические детали, которые позволяют использовать общую версию, когда конкретная версия не работает?

+0

В какой строке компилятор дает ошибку? – Gusman

+1

Попробуйте «эту IEnumerable коллекцию» - это должно работать без дженериков из-за ковариации IEnumerable . –

+0

@Dmitry Я подтвердил, что не работает, и существует другой метод расширения, который требует множественных перечислений, поэтому не является вариантом. – Phaeze

ответ

4

Это потому, что содержимое IList более специфично, чем позволяет подпись. Это может привести к нарушению подразумеваемого контракта.

Контракт IList<IUpdateableModel> что любой разработчик IUpdateableModel должен быть в состоянии быть добавлен в список. Это невозможно для List<ImplementationOfUpdateableModel>, поскольку вы можете добавлять только объекты типа ImplementationOfUpdateableModel.

Общая версия работает, потому что она позволяет методу принимать ILists более конкретного содержимого объекта.

+0

По какой-то причине я не связывал эти точки. – Phaeze

+0

Я думаю, что заявление «Это может привести к нарушению подразумеваемого контракта». следует переформулировать как «Это приводит к нарушению явного контракта». – Enigmativity

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