Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get the current type from a static method in abstract class?

How can I get the current Type in a static method that is defined in an abstract class?

Please note that as the method is defined in an abstract class, I can't use typeof.

Why would I want to do this? A possible usage is attributes. Consider the following example:

[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:

Here is the scenario.

Entity represents a 3D object. I'm writing a server software, that has a list of such entities. The server manually serializes the list and sends it to the client. As performance is highly important here, I'm not sending the type name. Every type of entity has a unique identifier, so when the client gets the data, it can efficiently deserialize it.

To create an instance of an entity, I'm doing something like:

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

The EntityRepository class looks like this:

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();
        }));
    }
}

The current Add<T> method has a parameter that represents the identifier.

But how would I write a Add<T> method that has no parameters - that recognizes the identifier automatically?

So I was thinking about adding an attribute to the nested Entity:

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

and a static property that returns the value of the Identifier attribute.

Then, an Add<T> method without parameters would look something like:

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

Note that in this case, I could just do T.GetType() to get the attribute, but that's not the point. How can I do that in the static property, Entity.Identifier?

like image 554
Alon Gubkin Avatar asked Jul 19 '11 14:07

Alon Gubkin


2 Answers

You can't, basically.

A call to Rock.Identifier will be resolved by the compiler to Entity.Identifier - there simply isn't any context in which to find the type.

A call to rock.Identifier won't even compile, as you're trying to access a static member through a variable.

The best workaround would depend on the real scenario - in the case you've posted, I'd suggest creating a static method elsewhere which accepted a type as a parameter.

Actually, you could fake it in a really horrible way for your specific scenario by using the compile-time type of Rock:

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

Then:

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

But this would not be polymorphic. For example:

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

You could change the extension method to call GetType() of course...

like image 132
Jon Skeet Avatar answered Nov 13 '22 17:11

Jon Skeet


Static methods are not polymorphic in C# - there's no way for subclasses to override this implementation.

EDIT: Essentially, as Jon Skeet points out, the C# compiler will treat a call to SubType.StaticMember as equivalent to to BaseType.StaticMember (unless of course the sub-type provides a new, hiding member with the same name). Consequently, this code will always run 'in the context' of the Identifier type, and there's no way to improve upon:

Type currentType = typeof(Identifier);

Personally, I don't like the fact that C# even lets you access static members 'through' subtypes - it just sets up nasty mistakes and misunderstandings.

like image 35
Ani Avatar answered Nov 13 '22 16:11

Ani