当 List<Interface> 对象属性的类继承自 interface 时,如何访问它?

How to access List<Interface> object properties when it's class is inherited from interface?

提问人:TheMostEpic 提问时间:12/13/2019 更新时间:12/13/2019 访问量:500

问:

好的,这就是问题所在。我有一个接口 IBook,其中包括属性 Name。有两个类继承自 IBook 并添加自己的属性 Genre。我想创建一个字典或一个列表,并在其中添加各种书籍,并通过字符串及其属性访问它们,所以我将其设置为字典。在示例中,我可以访问 books[“LOTR”]。名称但不是书籍[“LOTR”]。Genre,因为 Name 是 IBook 接口的属性,而 Genre 是从 IBook 继承的类的属性。

是否可以使 Dictionary 或 List 与接口类型一起使用,并且仍然能够访问所有继承的类属性,或者我应该使用数组或其他东西?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp124
{
interface IBook
{
    string Name { get; set; }
}

public class FantasyBook:IBook
{
    string name;
    string genre;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    public string Genre
    {
        get { return genre; }
        set { genre = value; }
    }
}
public class HorrorBook : IBook
{
    string name;
    string genre;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    public string Genre
    {
        get { return genre; }
        set { genre = value; }
    }
}
class Program
{
    static void Main(string[] args)
    {
        FantasyBook LordOfTheRings = new FantasyBook();
        HorrorBook Frankenstein = new HorrorBook();
        Dictionary<string, IBook> books = new Dictionary<string, 
IBook>();

        books.Add("LOTR", LordOfTheRings);
        books.Add("Frankenstein", Frankenstein);
        books["LOTR"].Name = "Lord Of The Rings";
        books["LOTR"].Genre = "Fantasy";
        Console.ReadLine();
    }
}
}
C# 数组 列表 字典 接口

评论

2赞 Charles 12/13/2019
如果它们都有名称和流派,那么为什么不将流派添加到界面中呢?您可以访问接口中未包含的所有属性,前提是将(books["LOTR"] as FantasyBook).Genre
2赞 Ousmane D. 12/13/2019
“是否有可能使 Dictionary 或 List 与接口类型一起使用,并且仍然能够访问所有继承的类属性”不,不可能。“或者我应该使用数组或其他东西吗?”问题不在于包含 IBook 的集合,所以不,再次使用数组不会解决任何问题。您的问题的解决方案可以通过制作定义的一部分来解决,因为这样做是有意义的。你为什么一开始就没有考虑过这一点?设计理由是什么?GenreIBook
1赞 Pavel Anikhouski 12/13/2019
您可以引入基抽象类,例如 with property(或将其添加到接口),然后将字典值强制转换为此类型以访问BaseBookNameName
1赞 RamblinRose 12/13/2019
按流派命名界面并具有“流派”属性很麻烦。闻起来很臭。
1赞 StackOverthrow 12/13/2019
顺便说一句,看看汽车属性。这个问题闻起来像是 2003 年学习 C# 的教授布置的家庭作业。

答:

1赞 timur 12/13/2019 #1

注释非常到位 - 你不能这样做,因为编译器会检查 上的可用成员(因为你声明了它),并且不会让你通过尝试访问未在那里定义的属性来搬起石头砸自己的脚。这是静态类型检查IBook

但是,让我们想象一下,您不关心类型安全性和性能。事实证明,你有一个选择。嗯,有点......因为您仍然必须放弃您的特定IBookdynamic

interface IBook {
    string Name { get; set; }
}

public class FantasyBook : IBook
{
    public string Name { get; set; }
    public string Genre { get; set; }
}
public class HorrorBook : IBook
{
    public string Name {get;set;}
    public string Genre {get;set;}
}

public class BadaBook : IBook // so I added this new class that does not implement Genre to illustrate a point
{
    public string Name { get; set; }
}
static void Main(string[] args)
    {
        var LordOfTheRings = new FantasyBook();
        var Frankenstein = new HorrorBook();
        var Badaboom = new BadaBook();
        Dictionary<string, dynamic> books = new Dictionary<string, dynamic>();

        books.Add("LOTR", LordOfTheRings);
        books.Add("Frankenstein", Frankenstein);
        books.Add("Badaboom", Badaboom);
        books["LOTR"].Name = "Lord Of The Rings";
        books["LOTR"].Genre = "Fantasy";
        books["Badaboom"].Name = "We can easily assign Name as it is defined. No problem here";
        books["Badaboom"].Genre = "But we will miserably fail here"; // RuntimeBinderException: 'UserQuery.BadaBook' does not contain a definition for 'Genre'

        Console.ReadLine();
    }

查看动态以进一步阅读。它伴随着我示例中概述的风险以及性能损失。它本身并不坏,只是需要适度服用。

评论

0赞 TheMostEpic 12/13/2019
帖木儿的回答应该有效。非常感谢!我知道我需要非常小心动态,而不是创建运行时错误问题。
3赞 John Alexiou 12/13/2019 #2

另一种方法是添加另一层接口,并使用模式匹配来访问属性:Genre

interface IBook
{
    string Name { get; set; }
}

interface IBookWithGenre : IBook
{
    string Genre { get; set; }
}

public class FantasyBook : IBookWithGenre
{
    public string Name { get; set; }
    public string Genre { get; set; }
}
public class HorrorBook : IBookWithGenre
{
    public string Name { get; set; }
    public string Genre { get; set; }
}

public class SimpleBook : IBook
{
    public string Name { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        FantasyBook LordOfTheRings = new FantasyBook();
        HorrorBook Frankenstein = new HorrorBook();
        SimpleBook abook = new SimpleBook();
        var books = new Dictionary<string, IBook>
        {
            { "LOTR", LordOfTheRings },
            { "Frankenstein", Frankenstein },
            { "Simple", abook },
        };
        books["LOTR"].Name = "Lord Of The Rings";
        if (books["LOTR"] is IBookWithGenre withGenre)
        {
            withGenre.Genre = "Fantasy";
        }
        Console.ReadLine();
    }
}