获取 属性 摘要中定义的值 class

Get property value defined in an abstract class

我正在开发一个非常简单的框架,我想在其中自动执行 CRUD 功能。出于这个问题的目的,我创建了下面的代码,经过简化只是为了说明我的问题。

所有项目都派生自(抽象)基础 class "DbItem"(其中包含 CRUD 函数),子 classes 提供额外的功能,并且还定义了 table 存储 DbItems 的名称。例如,"Equipment" 和 "Entity" 都派生自 DbItem,并定义 table 名称(分别为 "equipment" 和 "entities")。然而,"Entity" class 是抽象的,由 "Human" 和 "Animal" class 进一步推导出来。 (所有人类和动物都存储在共享 "entity" table 中,但设备存储在单独的 table 中。)

这部分代码有效。 DbItem 中定义的 Save() 方法正确解析了 DbTable 属性.

但是,我还有一个 DbCollection class,它扩展了通用 C# 集合 class。我希望 DbCollection 能够通过反射自动确定正确的数据库 table 名称。

因此,如果我想创建一个包含所有设备的列表,我会创建一个新的 DbCollection,代码组成适当的 SELECT 语句(SELECT ... FROM equipment ... ).这行得通。

但是,如果我想要所有实体(动物和人类)的列表,我有一个问题。 "Entity" 被标记为抽象的,我应该无法实例化它。但是,我也不知道如何获取 Entity.DbTable 属性.

的值

一个简单的"fix"是从实体class定义中删除限定的"abstract"。然而,这对我来说听起来不对。

你能告诉我如何获得 Entity.DbTable 属性 的值吗?

class Program
{
    abstract class DbItem
    {
        public int Id { get; set; }
        public abstract string DbTable { get; }

        public void Save()
        {
            Console.WriteLine($"INSERT INTO {DbTable} (Id) VALUES ({this.Id})...");
        }
    }

    abstract class Entity : DbItem
    {
        public sealed override string DbTable { get => "entities"; }
    }

    class Human : Entity { }

    class Equipment : DbItem
    {
        public override string DbTable => "equipment";
    }

    class DbCollection<T> : System.Collections.ObjectModel.Collection<T>
    {
        public virtual string DbTable { get
            {
                Type t = typeof(T);
                //System.Reflection.PropertyInfo p = t.GetProperty("DbName");

                if(t.IsAbstract)
                {
                    // What do we do here to get the value of Entity.DbTable?
                    var prop = t.GetProperty("DbTable");
                    // Herein lies the problem: One cannot instantiate an abstract class to provide to the GetValue() method
                    return prop.GetValue(null).ToString(); // System.Reflection.TargetException: 'Non-static method requires a target.'

                }
                else
                {
                    var obj = Activator.CreateInstance(t);
                    //return (obj as DbItem).DbTable; // this also works
                    var prop = t.GetProperty("DbTable");
                    return prop.GetValue(obj).ToString();
                }
            }
        }

        public DbCollection()
        {
            Console.WriteLine($"SELECT Id FROM {DbTable} WHERE ...");
        }

    }

    static void Main(string[] args)
    {
        var h = new Human();
        h.Save(); // 1. Correctly outputs "entities";

        var e = new Equipment();
        e.Save(); // 2. Correctly outputs "equipment";

        var ec = new DbCollection<Equipment>(); // 3. Correctly outputs "equipment"


        var hc = new DbCollection<Human>(); // 4. Correctly outputs "entities"

        var entityCollection = new DbCollection<Entity>(); // 5. Error.

        Console.ReadLine();

    }
}

您希望在您的第五个案例中发生什么?

绝对没有办法创建 abstract class 的实例。从实体 class 中删除 abstract 关键字。如果你想阻止 Entity class 的外部创建,你可以使用 internal 构造函数。

由于这是一个集合,您还可以使用集合的第一个条目来获得 DbTable 结果 - 这可能很危险,因为第二个条目可能是另一种类型。

不要使用 属性,使用属性。那就是你想要的,对吧?要将 class 与编译时固定的 table 名称相关联?

首先,创建一个存储 table 名称的自定义属性 class:

[AttributeUsage(AttributeTargets.Class | Inherited = true)]
public class DbTableAttribute: System.Attribute
{
    private readonly string _name;

    public string Name { get { return _name; } }

    public DbTableAttribute(string name)
    {
        _name = name;
    }
}

将其添加到您的 human/animal/entity/DbItem classes:

[DbTable("entities")]  
abstract class Entity : DbItem
{
    //Don't need this any more
    //public sealed override string DbTable { get => "entities"; }
}

然后像这样检索它:

public string GetDbTable<T>() where T : DbItem
{
    var attr = typeof(T).GetCustomAttributes(
        typeof(DbTableAttribute), true
    ).FirstOrDefault() as DbTableAttribute;
    return attr?.Name;
}