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

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(),第一个版本就可以正常工作。

那么是什么技术细节使通用版本在具体版本无法工作时起作用呢?


编译器在哪一行报错? - Gusman
1
尝试使用“this IEnumerable<IUpdateableModel> collection” - 这应该可以在不使用泛型的情况下工作,因为IEnumerable<T>具有协变性。 - Dmitry Razumikhin
@Gusman,我正在输入一份关于用法更详细说明的编辑。 - user1618236
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Jon Skeet
@JonSkeet 对不起,有时我发现很难把假设从我的头脑中排除。我认为我已经在这一点上添加了所有相关的内容。 - user1618236
显示剩余3条评论
1个回答

3
这是因为IList的内容比签名允许的更具体。这可能会违反隐含的契约。
IList的契约是,任何实现IUpdateableModel的对象都必须能够添加到列表中。但对于List来说,这是不可能的,因为你只能添加ImplementationOfUpdateableModel类型的对象。
泛型版本之所以可行,是因为它允许方法接受更具体对象内容的ILists。

出于某种原因,那很有道理,我没有将这些点连接起来。 - user1618236
我认为语句“This could result in a violation of the implied contract.”应该改为“This does lead to a violation of the explicit contract.”。 - Enigmativity

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接