是否可以对列表<接口>施加类型约束

本文关键字:施加 类型 约束 接口 列表 是否 | 更新日期: 2023-09-27 18:35:33

在我的类中,我有

class MyClass : IMyInterface
{
//I want MyClass only to be able to accept object of type List<SomethingElse>
public List<ISomething> ListOfSomethings {get; set;}
}
interface IMyInterface{
List<ISomething> ListOfSomethings {get; set;}
}
class SomethingElse : ISomething{
}
class SomethingMore : Isomething{
}

基本上,我想知道是否可以限制列表在MyClass中使用的类型,因此如果有人尝试将其编码为错误的类型(即Something More列表),它将引发异常。

编辑:如果这是不可能的,是否有可行的替代解决方案?

是否可以对列表<接口>施加类型约束

您可以使用

where限制来约束列表项(以及任何其他T)的T(类型):

有关更多详细信息,请参阅类型参数的约束

接口

interface ISomething { }

允许仅使用实现接口ISomethingT

interface IMyInterface<T> where T : ISomething
{
    List<T> ListOfSomethings { get; set; }
}

class SomethingElse : ISomething { }
class SomethingMore : ISomething { }
class MyClass1 : IMyInterface<SomethingElse>
{
    public List<SomethingElse> ListOfSomethings { get; set; }
}
class MyClass2 : IMyInterface<SomethingMore>
{
    public List<SomethingMore> ListOfSomethings { get; set; }
}

您可以将T限制在适合您的任何位置。例如,在类本身上。这只允许SomethingElse

class MyClass3<T> : IMyInterface<T> where T : SomethingElse
{
    public List<T> ListOfSomethings { get; set; }
}

一个带有Dictionary的示例:

var dic = new Dictionary<string, IMyInterface<ISomething>>();
dic.Add("MyClass1", (IMyInterface<ISomething>)new MyClass1());
dic.Add("MyClass2", (IMyInterface<ISomething>)new MyClass2());

如果您不会每次都强制转换它,那么我目前能想到的唯一解决方案是创建自定义字典并封装强制转换:

class MyDictionary : Dictionary<string, IMyInterface<ISomething>>
{
    public void Add(string key, MyClass1 value)
    {
        base.Add(key, (IMyInterface<ISomething>)value);
    }
    public void Add(string key, MyClass2 value)
    {
        base.Add(key, (IMyInterface<ISomething>)value);
    }
}
var dic2 = new MyDictionary();
dic2.Add("MyClass1", new MyClass1());
dic2.Add("MyClass2", new MyClass2());
//I want MyClass only to be able to accept object of type List<SomethingElse>

然后你不能把它定义为 List<ISomething> ,而是使用不同的接口甚至具体类型。如果将其定义为 List<ISomething> ,则它会自动接受实现ISomething接口的任何内容。这是没有办法的。

C#

语言设计上的类型安全,因此列表的使用者无法将不SomethingElseSomethingMore的类型注入其中。

如果您需要限制从公共接口派生的某些类型,例如SomethingElse,我会去

  1. 隐藏公开List本身

    的属性
    private List<ISomething> ListOfSomethings {get; set;}
    
  2. 添加公共成员函数,例如FromList

    public void FromList(List<SomethingElse> somethings)
    {
        ListOfSomethings = somethings;
    }
    

此函数成为将列表分配给类的唯一方法,并且考虑到它仅接受某种类型的列表,因此创建了所需的限制。

您可以使用显式接口实现来执行此操作:

class MyClass : IMyInterface
{
    List<ISomething> IMyInterface.ListOfSomethings
    {
        get { return this.ListOfSomethings.Cast<ISomething>().ToList(); }
        set { this.ListOfSomethings = value.Cast<SomethingMore>().ToList(); }
    }
    List<SomethingMore> ListOfSomethings { get; set; } 
}

请注意,不建议进行此类限制,因为这违反了 Liskov 替换原则:类的用户可能正在使用 IMyInterface 接口,并且不知道其类型受到限制。

另一个问题是在接口中公开这样的List<T>:调用方可以调用列表方法(如 Add 或 Delete),也可以设置整个 List 实例。这可能不是您想要的。如果要公开只读集合,请使用具有数组或可枚举类型的 getter。