任何方式来实现类似于"抽象静态成员"

时间:2014-03-19 08:58:03

标签: c# inheritance abstract member

我有一个存储库类,它使用NPoco / PetaPoco来访问从公共内容表中读取的数据。

create table Content (
    Id int not null identity primary key,
    Type tinyint not null,
    ...
)

然后我有一个其他类型继承的抽象ContentBase类。继承类型之间的主要区别是该类型DB列值的值。每个具体的内容类型确实有一些额外的列,但这里没有相关性。

因此。为了让我的存储库类返回我想编写的任何实际的具体类:

public TContent Create<TContent>(string name, ...)
{
    ...
    // SP executes a few insers and returns newly created data instance
    return db.Single<TContent>(
        new Sql.Builder.Append(";exec dbo.CreateContent @Type, @Name, ...", new {
            Type = TContent.Type, // this is the problem
            Name = name,
            ...
        }));
}

正如您所看到的,我将需要我的基本抽象类来定义静态成员以获取应该可以通过泛型类型规范访问的实际类型值。继承类应该根据它们的具体类型实现来设置它。

问题当然是在C#中没有abstract static成员这样的事情。

我应该如何以某种方式处理此问题,以便我的存储库能够自己提供Type值,而无需我明确地提供调用?我只想在调用它时提供泛型类并返回正确的具体类型。

为什么它必须是静态成员?

我的方法没有得到特定类型的对象实例,但它应该创建一个。这就是为什么我无法真正拥有实例成员Type并在执行我的存储库方法时从该成员读取的内容。

可能的开始

由于静态成员在所有实例之间共享,并且如果这是基类,所有继承类共享相同的成员,除非此类是通用的。在这种情况下,我们得到每个泛型类型的静态成员。

所以我想在基础抽象和具体类之间添加一个额外的类:

public abstract class ContentBase
{
    ...
}

public abstract class ContentBase<TConcrete>
    where TConcrete: ContentBase<ContentBase> // is this constraint ok?
{
    public static ContentType Type = ???;
}

然后是具体的课程:

public class ContentOne : ContentBase<ContentOne>
{
    ???
}

如上所述,我应该能够将我的存储库方法称为:

repo.Create<ContentOne>(name, ...)

此方法库中应该能够访问调用提供的泛型类型的静态成员...

2 个答案:

答案 0 :(得分:0)

即使您想要拥有一个抽象的ContentBase类,您也无法访问每个派生类的自定义行为;这是一个小测试我试图看看你的想法是否可以使用:

public abstract class ContentBase<T>
{
    public static Func<string> TypeLocator { get; set; }
    static ContentBase()
    {
        TypeLocator = () => typeof(T).Name;
    }

}

public class Content1 : ContentBase<Content1>    {
    private static string Content1Type = "The type of Content 1";
    static Content1()
    {
        TypeLocator = () => Content1Type;
    }
}

public class Content2 : ContentBase<Content2>    {}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Content1.Type => " + GetObject<Content1>()); // Content1
        Console.WriteLine("Content2.Type => " + GetObject<Content2>()); // Content2
    }

    private static string GetObject<TContent>() where TContent: ContentBase<TContent>
    {
        var typeLocator = typeof(ContentBase<TContent>).GetProperties()[0].GetValue(null, null) as Func<string>;
        return typeLocator.Invoke();
    }
}

您可以获得的最好的是TContent的类型,这可能足以让您探索下面的映射命题。

我并没有真正看到实现抽象静态成员的方法,但是您可以在整个程序中使用外部注册字典作为单例,它将用作TypeType.DBType之间的映射器。你的public TContent Create<TContent>(string name, ...) { ... // SP executes a few insers and returns newly created data instance return db.Single<TContent>( new Sql.Builder.Append(";exec dbo.CreateContent @Type, @Name, ...", new { Type = RegisteredTypes[typeof TContent], Name = name, ... })); }

{{1}}

答案 1 :(得分:0)

不幸的是,“你做不到!”的答案似乎有一个具体的原因,直到直接支持C#8为止,没有明显的解决方法。

非静态类的静态成员与非静态类的静态成员并不真正属于同一类。静态部分实际上属于完全不同的类(静态类),其名称由编译器组成,并且只有编译器才知道。

编译器隐藏此内容的愚蠢行为使您认为应该可行。如果编译器没有隐藏实际上不存在具有静态和非静态成员的类的事实,那么很明显,静态类的名称对于非静态类是未知的。无法直接获得对您的班级引用的任何其他班级的引用。

从C#-8开始,您可以在接口中定义静态方法(因此可以定义属性,但不能定义字段),因此,如果您不被遗留在传统障碍后面,则可以彻底解决问题。