Как я могу получить текущий тип из статического метода в абстрактном классе? - PullRequest
7 голосов
/ 19 июля 2011

Как я могу получить текущий 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);

EDIT:

Вот сценарий.

Сущность представляет собой трехмерный объект.Я пишу серверное программное обеспечение, у которого есть список таких объектов.Сервер вручную сериализует список и отправляет его клиенту.Поскольку производительность здесь очень важна, я не посылаю имя типа.Каждый тип объекта имеет уникальный идентификатор, поэтому, когда клиент получает данные, он может эффективно десериализовать их.

Чтобы создать экземпляр объекта, я делаю что-то вроде:

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<T> имеет параметр, который представляет идентификатор.

Но как мне написать Add<T> метод, который не имеет параметров - который распознает идентификатор автоматически?

Итак, я думал о добавлении атрибута к вложенному Entity:

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

и статического свойства, которое возвращает значение атрибута Identifier.

Тогда метод Add<T> без параметров будет выглядеть примерно так:

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

Обратите внимание, что в этом случае я мог бы просто сделать T.GetType(), чтобы получить атрибут, но это не главное.Как я могу сделать это в статическом свойстве Entity.Identifier?

Ответы [ 4 ]

10 голосов
/ 19 июля 2011

Вы не можете, в основном.

Вызов 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(), конечно ...

7 голосов
/ 19 июля 2011

Статические методы не полиморфны в C # - у подклассов нет возможности переопределить эту реализацию.

РЕДАКТИРОВАТЬ: По сути, как отмечает Джон Скит, компилятор C # будет обрабатывать вызов SubType.StaticMember как эквивалент BaseType.StaticMember (если, конечно, подтип не предоставляет new скрывающий член с таким же именем).Следовательно, этот код всегда будет выполняться «в контексте» типа Identifier, и нет способа улучшить его:

Type currentType = typeof(Identifier);

Лично мне не нравится тот факт, что C # даже позволяет вамполучить доступ к статическим членам «через» подтипы - он просто устанавливает неприятные ошибки и недоразумения.

0 голосов
/ 08 января 2014

C# не поддерживает полиморфизм для статических членов.Статические члены не могут быть виртуальными или включаться в интерфейсы.Таким образом, вы не можете легко группировать классы, даже если все они имеют некоторый статический член с одинаковым именем и подписью.

0 голосов
/ 19 июля 2011

Самый простой способ - использовать дженерики:

[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<Rock>.Identifier.Пример использования этого шаблона сделан в замке ActiveRecord.

...