获取抽象类

时间:2017-08-05 09:57:35

标签: c# inheritance abstract derived-class

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

所有项目都来自(抽象)基类" DbItem" (包含CRUD函数),子类提供附加功能,并定义存储DbItem的表名。例如,"设备"和"实体"两者都来自DbItem,并定义表名("设备"和"实体"分别)。然而,"实体" class是抽象的,并且进一步由" Human"和"动物"类。 (所有人类和动物都存储在共享"实体"表中,但设备存储在单独的表中。)

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

然而,我还有一个DbCollection类,它扩展了通用的C#Collection类。我希望DbCollection能够使用反射自动确定正确的数据库表名。

因此,如果我想创建所有设备的列表,我创建一个新的DbCollection,代码组成相应的SELECT语句(SELECT ... FROM equipment ...)。这很有效。

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

一个简单的"修复"是删除"摘要"从Entity类定义中限定。然而,这对我来说听起来不对。

请告诉我如何获取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();

    }
}

2 个答案:

答案 0 :(得分:1)

Don't use a property, use an attribute. That's what you want, right? To associate a class with a table name that is fixed at compile time?

First, create a custom attribute class that stores a table name:

[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;
    }
}

Add it to your human/animal/entity/DbItem classes:

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

And retrieve it like this:

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

答案 1 :(得分:0)

您希望在第五种情况下发生什么?

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

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