为什么这个扩展方法适用于泛型但不适用于集合基类型?

时间:2016-02-26 22:17:21

标签: c# generics inheritance

在我的项目中,我有以下类结构:

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(),则第一个版本可以正常运行。

那么,当具体版本没有时,哪些技术细节允许通用版本工作?

1 个答案:

答案 0 :(得分:4)

这是因为IList内容比签名允许的更具体。这可能会导致违反隐含合同。

IList<IUpdateableModel>的合同是IUpdateableModel的任何实施者都必须能够添加到列表中。 List<ImplementationOfUpdateableModel>无法做到这一点,因为您只能添加ImplementationOfUpdateableModel类型的对象。

泛型版本有效,因为它允许该方法接受IList更具体的对象内容。