是否可以从C#中的List <T>和Collection <T>分配类型?

时间:2019-10-10 11:31:59

标签: c# collections polymorphism

我在C#中有以下两个类:

class DbModel{
    public List<string> Values {get; set;}
    //...other proerties...
}

class ViewModel{
    public ObservableCollection<string> Values {get; set;}
    //...other proerties...
}

我想创建两个类都实现的公用Interface,因此我可以通过某些方法传递这两个中的任何一个。这有可能吗?我尝试过类似的事情:

public interface IModel{
    IEnumerable<string> Values {get; set;}
}

但是,即使List和ObservableCollection<string>都可以分配给IEnumerable<string>,这还是行不通的。

有没有解决的办法?最好的解决方法是什么(如果有)?

2 个答案:

答案 0 :(得分:2)

C#没有协变返回类型(尽管它们是have been proposed)。因此,您需要使用显式接口实现(如Lasse所述)。

您可能不希望设置Values属性。如果它是可设置的,那么即使基础IEnumerable<string> / ViewModel仅支持非常特定的类型,任何人都可以设置实现DbModel的任何类型。

public interface IModel
{
    IEnumerable<string> Values { get; }
}

public class ViewModel : IModel
{
    public ObservableCollection<string> Values { get; set; }
    IEnumerable<string> IModel.Values => Values;
}

答案 1 :(得分:2)

我之前已经尝试过,创建适用于各种实体的漂亮的“域”接口,因此您可以编写在该接口上而不是在域实体上运行的代码,但是您很快就会遇到问题。

对于初学者,当类实现接口时,它必须与接口的成员类型完全匹配。当然,List<T>ObservableCollection<T>都实现了IEnumerable<T>,但这并不意味着它们都满足该接口成员的声明。

因此,您必须实现该特定类型。例如,您可以使用显式接口实现。但是接口成员既有getter也有setter:

class DbModel : IModel
{
    public List<string> Values {get; set;}

    IEnumerable<string> IModel.Values
    {
        get { return Values; }
        set
        {
            Values = ... // now what?
        }
    }
}

class ViewModel : IModel
{
    public ObservableCollection<string> Values {get; set;}

    IEnumerable<string> IModel.Values
    {
        get { return Values; }
        set
        {
            Values = ... // now what?
        }
    }
}

您可能需要设置器,因为您希望一些通用代码能够分配Values集合。 IEnumerable<T>除枚举外,对其他任何事情都没有真正的作用。

因此,您必须将传入的values转换为与setter中的类相关的类型。例如前者中的Values = values.ToList()

然后您有一个呼叫者:

IModel dbModel = new DbModel();

var values = new List<string> { "foo", "bar" };

dbModel.Values = values;

values.Add("baz");

此代码后dbModel.Values将是什么?提示:它不会包含"baz"。不要采取这种方法,它会咬你。

忘记通用接口。确定接口应该解决的实际问题,并以其他方式解决。