【问题标题】:How to access List<Interface> object properties when it's class is inherited from interface?类从接口继承时如何访问 List<Interface> 对象属性?
【发布时间】:2019-12-12 21:28:47
【问题描述】:

好的,问题来了。我有一个接口 IBook,其中包括属性名称。有两个类从 IBook 继承并添加自己的属性 Genre。我想创建一个字典或列表并在那里添加各种书籍并通过字符串及其属性访问它们,所以我将它设为字典。在示例中,我可以访问 books["LOTR"].Name 但不能访问 books["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();
    }
}
}

【问题讨论】:

  • 如果他们都有名字和流派,那你为什么不在界面中添加流派呢?如果您投射(books["LOTR"] as FantasyBook).Genre,您可以访问所有不在界面中的属性
  • “是否可以使 Dictionary 或 List 与接口类型一起工作,并且仍然能够访问所有继承类属性”不不可能。 “或者我应该使用数组还是什么?”问题不在于包含 IBook 的集合,所以再次使用数组不会解决任何问题。您的问题的解决方案可以简单地通过将Genre 作为IBook 定义的一部分来解决,因为这样做很有意义。为什么你一开始没有考虑到这一点?设计理由是什么?
  • 您可以引入基本抽象类,例如带有Name 属性的BaseBook(或将其添加到接口),然后将字典值转换为该类型以访问Name跨度>
  • 按流派命名接口并具有“流派”属性很麻烦。味道不好。
  • 顺便说一句,看看auto properties。这个问题闻起来像是一位 2003 年学习 C# 的教授布置的作业。

标签: c# arrays list dictionary interface


【解决方案1】:

另一种方法是使用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();
    }
}

【讨论】:

    【解决方案2】:

    cmets 非常正确 - 你不能这样做,因为编译器会检查 IBook 上的可用成员(因为你声明了它),并且不会让你通过尝试访问一个属性来打自己的脚没有在那里定义。这是static type checking

    但是让我们再想象一下,您并不关心类型安全和性能。事实证明,你有一个选择。好吧,有点...因为您仍然必须放弃您的特定 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();
        }
    

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

    【讨论】:

    • timur 的回答应该有效。太感谢了!我知道我需要非常小心动态,而不是创建运行时错误问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多