【问题标题】:How can I get the current type from a static method in abstract class?如何从抽象类的静态方法中获取当前类型?
【发布时间】:2011-07-19 14:57:43
【问题描述】:

如何在抽象类中定义的静态方法中获取当前的Type

请注意,由于方法是在抽象类中定义的,我不能使用typeof

我为什么要这样做?一个可能的用法是属性。考虑以下示例:

[Identifier(1000)]
public class Rock : Entity { }

public abstract class Entity
{
    public static ushort Identifier
    {
        get
        {
            // How do I get here the current type of the object?
            Type currentType = ...;

            // in a non-static method or property, I'd do:
            // Type currentType = this.GetType();

            foreach (IdentifierAttribute attribute in currentType.GetCustomAttributes(true))
                return attribute.Identifier;

            throw new EntityException("No identifier has specified for this type of entity.");
        }
    }
}

Rock rock = new Rock();

// should print 1000
Console.WriteLine(rock.Identifier);

编辑:

这里是场景。

实体表示一个 3D 对象。我正在编写一个服务器软件,其中包含此类实体的列表。服务器手动序列化列表并将其发送给客户端。由于性能在这里非常重要,因此我不会发送类型名称。每种类型的实体都有唯一的标识符,因此当客户端获取数据时,它可以高效地反序列化。

要创建实体的实例,我正在执行以下操作:

Entity entity = EntityRepository.Instance.CreateNew(identifier);

EntityRepository 类如下所示:

public sealed class EntityRepository
{
    private static readonly Lazy<EntityRepository> lazy =
        new Lazy<EntityRepository>(() => new EntityRepository());

    IDictionary<ushort, Func<Entity>> _repo;

    private EntityRepository()
    {
        _repo = new Dictionary<ushort, Func<Entity>>();
    }

    public static EntityRepository Instance 
    { 
        get { return lazy.Value; } 
    }

    public Entity CreateNew(ushort id)
    {
        return _repo[id]();
    }

    public void Add<T>(ushort id)
        where T : Entity, new()
    {
        _repo.Add(id, new Func<Entity>(() =>
        {
            return new T();
        }));
    }
}

当前的Add&lt;T&gt; 方法有一个代表标识符的参数。

但是我将如何编写一个没有参数的Add&lt;T&gt; 方法 - 自动识别标识符?

所以我在考虑给嵌套的Entity添加一个属性:

[Identifier(1000)]
public class Rock : Entity { }

以及返回Identifier 属性值的静态属性。

然后,没有参数的Add&lt;T&gt; 方法看起来像:

public void Add<T>(ushort id)
    where T : Entity, new()
{
    _repo.Add(T.Identifier, new Func<Entity>(() =>
    {
        return new T();
    }));
}

请注意,在这种情况下,我可以使用T.GetType() 来获取属性,但这不是重点。我怎样才能在静态属性Entity.Identifier 中做到这一点?

【问题讨论】:

  • Identifier属性和Identifier属性有什么关系?
  • 我已编辑问题以澄清这一点。
  • 您说可以使用T.GetType() 不是“重点”(尽管您实际上的意思是typeof(T))-但是如果这样可以解决您的问题,为什么不使用它呢?您尝试这样做的方式根本行不通 - 所以请使用确实有效的方式。

标签: c# static attributes types


【解决方案1】:

你不能,基本上。

Rock.Identifier 的调用将由编译器解析为Entity.Identifier - 根本没有任何上下文可以找到类型。

rock.Identifier 的调用甚至无法编译,因为您正试图通过变量访问静态成员。

最好的解决方法取决于 real 场景 - 在您发布的情况下,我建议在其他地方创建一个静态方法 接受 一个类型作为参数。

实际上,您可以通过使用 Rock 的编译时类型以非常可怕的方式针对您的特定场景伪造它:

public static class NastyExtensions
{
    public static ushort GetIdentifier<T>(this T actualValueIsIgnored)
    {
        Type type = typeof(T);
        ... code as before
    }
}

然后:

Rock rock = new Rock();
Console.WriteLine(rock.GetIdentifier());

但这不会是多态的。例如:

Entity rock = new Rock();
Console.WriteLine(rock.GetIdentifier()); // Bang! T would be Entity here

当然你可以把扩展方法改成调用GetType()...

【讨论】:

  • "can't" 有点苛刻,你可以(我们都提供了一种方法),但可能有更好的方法来解决这个问题。事实上,我有一个扩展方法T GetClassAttr&lt;T&gt;(this object o),我在类似这个问题的代码的几个地方使用它。
  • 我已经用真实场景编辑了这个问题。也许有人会想出一个主意?
  • @Bill:你不能做最初要求的事情:获取在基类的静态成员中使用的“实际”类型。有一些解决方法可以实现“类似的效果”,但您无法获得调用静态成员的类型。
  • 虽然不完全是我想要的,但我必须接受这个答案,因为它是问题的最正确答案。谢谢。
【解决方案2】:

静态方法在 C# 中不是多态的 - 子类无法覆盖此实现。

编辑: 本质上,正如 Jon Skeet 指出的那样,C# 编译器将对 SubType.StaticMember 的调用视为等同于 BaseType.StaticMember(当然,除非子类型提供 new,隐藏成员具有相同的姓名)。因此,此代码将始终在 Identifier 类型的“上下文”中运行,并且无法改进:

Type currentType = typeof(Identifier);

就我个人而言,我不喜欢 C# 甚至允许您“通过”子类型访问静态成员 - 它只会造成严重的错误和误解。

【讨论】:

  • 对。这意味着Identifier 中的“当前类型”将始终为Entity
  • 请注意,您可以利用 C# 的这个怪癖,因为 SubType.StaticMember 将被编译为 BaseType.StaticMember 并指定泛型类型参数。因此,如果BaseType 有一个泛型参数,您指定它是您想要的子类型,您可以在方法中获取子类型。
【解决方案3】:

最简单的方法是使用泛型:

[Identifier(1000)]
public class Rock : Entity<Rock> { }

public abstract class Entity<T>
{
    public static ushort Identifier
    {
        get
        {
            // How do I get here the current type of the object?
            Type currentType = typeof(T);

            foreach (IdentifierAttribute attribute in currentType.GetCustomAttributes(true))
                return attribute.Identifier;

            throw new EntityException("No identifier has specified for this type of entity.");
        }
    }
}

这样,当您调用Rock.Identifier 时,编译器会将其解析为Entity&lt;Rock&gt;.Identifier。此模式的示例用法在 Castle ActiveRecord 中完成。

【讨论】:

  • 或者只是使方法通用-可能更干净。
  • 这是一个不错的解决方案,但没有任何意义:(
  • 如果约束能够正常工作,这将是一个很好的解决方案,例如Entity&lt;T&gt; where T : Entity&lt;T&gt;。可悲的是,它们不起作用,像class Foo : Entity&lt;Bar&gt; 这样的错误是一场噩梦。它们仅适用于运行时类型构造...
  • @Agent_L 实际上,我得到了一个 public abstract class PersistentObject&lt;T&gt; where T : PersistentObject&lt;T&gt;, new(),它运行良好,甚至允许将 T 作为返回类型的静态变量。我没有看到你的问题。
  • @Nyerguds 再次阅读我的评论。我的咆哮是关于约束不够约束。它工作得很好,因为它甚至在它不应该工作的地方也能工作。
【解决方案4】:

C# 不支持静态成员的多态性。静态成员不能是虚拟的或包含在接口中。 因此,即使所有类都有一些具有相同名称和签名的静态成员,您也无法轻松地对类进行分组。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-07-03
    • 2011-09-26
    • 1970-01-01
    • 2012-10-18
    • 2012-12-03
    • 2022-10-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多