【问题标题】:get the class type in a self referencing, generic class, static constructor在自引用、泛型类、静态构造函数中获取类类型
【发布时间】:2016-05-02 07:13:23
【问题描述】:

好的,这不是一个简单的问题,代码将帮助我解释问题。

我需要的是一种在自引用、泛型类、静态构造函数中获取类类型的方法

想象一下你有这样的代码:

public class ClassA : BaseClass<ClassA>{
}
public abstract class BaseClass<T> where T : BaseClass<T> {
    static readonly int _aValue;
    static BaseClass(){
       //here i have code that load _avalue for each kind of T type, based on my own logic
       _aValue=1;

       //need to get the real type here
    }
    public int GetValue() {
       return _aValue;
    }
}


ClassA c = new ClassA();
Console.WriteLine(c.GetValue());

然后当你做类似的事情时

ClassA c = new ClassA();
Console.WriteLine(c.GetValue());

这里发生的情况是静态构造函数被调用,并且在我的示例中将值加载到静态变量

我需要在静态构造函数中知道调用构造函数的类型(在泛型类型构造函数中,每个派生类都会调用它,所以我假设我可以做我需要的)如果 T 类型实际上是 ClassA

我真正需要的是知道我是否正确声明了我的类定义,因为我还没有找到确保自引用类型是真正自引用的好方法,因为在我的测试中我发现

ClassX : Message<ClassAAA>

在我想强制执行时不会出现错误

ClassA : Message<ClassA>
ClassB : Message<ClassB>
ClassC : Message<ClassC>

而不是

ClassD : Message<DifferentClass>

有什么线索吗?

我已经尝试过使用 MethodInfo.GetMethod().DeclaringType 但它返回基类

更新 好的,让我试着解释一下 在我的场景中,我使用这种设计来定义我在客户端/服务器场景中使用的自定义消息定义,并且每个不同的消息派生类必须有一个指定“消息 id”的字节,所以我使用属性来装饰我的类并且该属性允许我指定一个枚举值(所以我在代码周围没有幻数)

在我的 Message 基类的静态构造函数中,我使用反射来读取属性值并将其存储到其静态 _aValue 成员中,因此每种 Message 都有自己的消息 id,并且每种消息类型仅加载一次,并且出于性能原因,不在实例构造函数期间。 它工作得很好,我可以说一个像这样的类

[MyAttribute(MyMessages.Ping)]
public class PingMessage : Message<PingMessage>

每当我的 PingMessage 类被实例化时,我都可以获得它的静态 messageTye 值,我对此很满意,但问题是有时会因为一个错误而创建一个类似的类

[MyAttribute(MyMessages.Ping)]
public class PingMessage : Message<AnotherMessage>

并且通用约束不会引发编译时错误,因为 AnotherMessage 是另一个从 Message 继承的类,因此它是合法的,但我想强制执行,如果您从 Message 继承,则 T 必须是继承的类,所以只允许

PingMessage : Message<PingMessage>

我知道没有允许我这样做的通用约束,我当然不能在约束中添加派生类,因为我不知道我将创建哪些消息(而且无论如何有很多消息它没有意义)所以我想在消息类型的静态构造函数期间进行类型约束检查,因为我已经在那里做了一些事情,所以在我看来这是放置检查的最佳位置,上升如果类型没有错误或没有在类上指定属性,则会出现异常

从技术上讲,我可以在我的实例构造函数中进行这种检查,但性能很重要,所以我不能。

我曾想过让我的 Message 实现一个自定义接口,然后使用该接口的方法来检查类型,并仅在初始化期间调用它,但如果可能的话,我一直在寻找一种更简单的方法,但似乎反射可以'在这里帮不了我

【问题讨论】:

  • if (c is ClassA)? MSDN -- 该示例似乎与您正在寻找的模式相匹配。
  • 您无法在静态构造函数中知道这一点,因为它是静态的,不会被任何类型“调用”。如果您在实例构造函数中执行此操作并在类不是自引用的情况下抛出异常怎么办?
  • 你能解释一下为什么你需要在静态构造函数而不是实例构造函数中这样做吗?
  • 请注意BaseClass&lt;X&gt; 的静态构造函数对于派生自BaseClass&lt;X&gt; 的所有类只会被调用一次。例如,如果您有ClassB : BaseClass&lt;ClassA&gt;ClassA : BaseClass&lt;ClassA&gt;,那么静态构造函数将只针对这两种类型一起执行一次。
  • 添加了更新以更好地解释问题以及为什么建议的答案不适用

标签: c# generics


【解决方案1】:

我不知道有什么方法可以阻止 class X : Message&lt;Y&gt; 被声明,但是您可以将 Message 子类型的使用限制为只使用您想要的那些。

R Foo<A>(A a) where A : Message<A>
{
  // …
}

如果您使用类型反射,只有在运行时才能知道您的程序是否有效。以编译器可以检查的解决方案为目标。

【讨论】:

  • 阅读我帖子中的更新。我把我的检查放在静态构造函数中,即使因为我必须在运行时运行它(它很好,如果条件不满足,它会引发异常)但是出于性能原因我不能在实例构造函数中运行它,问题是静态构造函数 afaik 不公开当前的类类型,这就是我正在寻找的替代方法
  • @FabioAngela 我不明白为什么[MyAttribute(MyMessages.Ping)] 属性是相关的。我的建议不需要静态构造函数或运行时检查。也许它不适合您的问题,但我现在不确定问题是什么。
【解决方案2】:

第一次访问基类时调用基类的静态构造函数。这不必是派生类实例化,例如在基类上调用静态方法时。派生类与基类的静态构造函数毫无关系。也不可能,因为多个类可以派生自同一个泛型基类。

派生类实例化是基类可以知道您正在处理哪个派生类的最早时间。您可以在非静态基类构造函数中测试对象的运行时类型:if (!typeof(T).IsAssignableFrom(this.GetType())) throw new InvalidOperationException();。当然,缺点是它没有在编译时被捕获。

【讨论】:

  • 阅读我帖子中的更新。我不能在实例方法中运行它,并且顺便说一句,在泛型类中,为每个不同的 T 调用一次静态构造函数(我已经在我的设计中利用了它)
  • 基类的静态构造函数为每个T 调用,但不是为每个派生类调用。 IE。如果多个类派生自同一个Base &lt;T&gt;,它只会执行一次。基类的静态构造函数中根本没有派生类,尽管您可以使用反射来找到它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-16
  • 2020-11-11
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多