【问题标题】:Any way to implement something that would resemble "abstract static member"任何实现类似于“抽象静态成员”的方法
【发布时间】:2014-04-25 09:21:58
【问题描述】:

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

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 members 这样的东西

我应该如何解决这个问题,以便我的存储库能够自行提供 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, ...)

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

【问题讨论】:

  • 这不是可以使用抽象工厂解决的吗,比如 Windsor 中使用的 TypedFactory? docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx 在运行时确定解析对象的类型在哪里?
  • @sammy:类型可以在运行时解析,但泛型会给我编译时间解析。这样做我并没有真正受益。

标签: c# inheritance abstract member


【解决方案1】:

即使您有一个抽象的 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 的类型,这可能足以让你探索下面的映射命题。

我真的没有看到实现抽象静态成员的方法,但是您可以求助于在整个程序中作为单例存在的外部注册字典,该字典将用作您的 Type 和您的 Type.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,
            ...
        }));
}

【讨论】:

  • 有趣的是,即使我为在我的泛型类型上定义静态类的某些类型提供了类型约束,除非我使用具体类型,否则我仍然无法访问此静态成员。所以不可能访问TContent.StaticMember...这很奇怪。否则我已经非常接近成功了。最后一个障碍阻止了我成功。
  • 是的,这就是我在设置上面的测试时发现的(带有 TypeLocator Func 的那个)。不调用具体类的静态构造函数。我敢肯定这是有原因的,但我没有看过文献。我认为注册是必要的,但我希望看到另一个解决方案弹出
  • 我试图做到这一点this way 但前面提到的障碍阻止了我让它工作。基本上,我公开了一个静态属性,该属性读取静态字段而不是链接示例实现的委托。我实现的每个类都将静态构造函数中的静态字段设置为适当的值。但是TContent.StaticMember 不可见... :(
【解决方案2】:

不幸的是“你不能!”的答案。似乎有一个具体的原因,在直接支持它的 C# 8 之前没有明显的解决方法。

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

编译器为了隐藏这一点而采取的轻描淡写使您误以为它应该是可能的。如果编译器没有隐藏这样一个事实,即确实不存在同时具有静态和非静态成员的类,那么更明显的是静态类的名称对于非静态类是未知的。与您的类引用的任何其他类的引用一样,不能直接使用。

从 C#-8 开始,您可以在接口中定义静态方法(因此可以定义属性,但不能定义字段),因此如果您没有被困在遗留障碍后面,问题就完全解决了。

【讨论】:

    猜你喜欢
    • 2010-10-23
    • 2013-08-02
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 2012-12-03
    • 2021-01-10
    • 1970-01-01
    相关资源
    最近更新 更多