Static property of type parameter - is this a CLR or C# limitation?

O

Ole Nielsby

Here comes a generic class with a static property.
Let's say they are exotic singleton pets.


abstract class Pet {...}

abstract class SharedPet<T>: Pet
where T: SharedPet<T>, new()
{
private static T singleton = new T();
public T Singleton {get {return singleton;}}
}

class KingKong : SharedPet<KingKong> {...}
class Gozilla : SharedPet<Gozilla> {...}


I use pets as a parameter to another generic class:


class PetLover<T>
where T : Pet, new()
{
public virtual T GetAPet
{ return new T(); } //Let it rain cats and dogs...
}

class SharedPetLover<T> : PetLover<T>
where T : SharedPet<T>, new()
{
public override T GetAPet //...but not gozillas
{ return T.Singleton; }
// Compile time error:
// 'T' is a 'type parameter', which is not
// valid in the given context'
}

Unforunately, it seems I can't access a static member
of T, though the constraint ensures it is there.

Is this a restriction of C#, or is it a CLR thing?
Is there a neat way of getting around it, or am I left
with the options of either rewriting the GetAPet
override using reflection, or doing copy-paste
overrides in KingKongLover, GozillaLover,
KermitLover, ReptarLover...?


(In case anyone wonders what the "real beasts"
look like: I'm trying to do a continuation passing
style implementation of a programming language
with a DOM like data model, and the nodes used
to implement continuations etc. must be as slim
as possible, yet compliant with the data model.)
 
B

Barry Kelly

Ole Nielsby said:
abstract class SharedPet<T>: Pet
where T: SharedPet<T>, new()
{
private static T singleton = new T();
public T Singleton {get {return singleton;}}

This property is not static - but it doesn't affect your situation.

A more concise summary of the situation, I think, is this:

---8<---
class A<T>
{
public static T Value
{
get { return default(T); }
}
}

class B<T,U>
where T : A<U>
{
public U Value
{
get { return T.Value; }
}
}
--->8---
Unforunately, it seems I can't access a static member
of T, though the constraint ensures it is there.

Is this a restriction of C#, or is it a CLR thing?

When a generic class is being compiled to MSIL (CIL), every reference to
a method or field (properties are accessed via get and set methods) gets
turned into a metadata token, one of MethodRef, MethodDef, FieldDef,
FieldRef (*Ref come from other assemblies, *Def are defined in the same
assembly).

Instance members are known from constraints because there is an actual
metadata token corresponding to each the base class constraint's members
(if any) and to each method defined in each interface constraint.

To the best of my knowledge, there's no way in IL to represent an as-yet
unbound methodref/methoddef in the way that you require. In the raw
assembly, the tokens are referred to via integer keys into a dictionary
associated with the assembly - thus the representation doesn't encode
what would be required in your case, somehow passing the generic type
argument to the target generic metadata token.

So, I would say it is a current architectural limitation of the CLR.
Is there a neat way of getting around it, or am I left
with the options of either rewriting the GetAPet
override using reflection, or doing copy-paste
overrides in KingKongLover, GozillaLover,
KermitLover, ReptarLover...?

I would suggest using a factory object approach. If you're familiar with
languages that support metaclasses (Delphi, Python, Smalltalk, etc.),
implement something similar. Basically, turn your static members into
instance members on a separate singleton class. You might need to pass
an extra parameter to constructors in your generic classes to support
this.

Alternatively, if you can take the performance hit, you might attribute
your classes with the relevant data you need, and pull that information
out via reflection.
(In case anyone wonders what the "real beasts"
look like: I'm trying to do a continuation passing
style implementation of a programming language

Good luck implementing it on .NET - the best I could come up with when
implementing something similar was a registry of methods, and returning
the next (method, arguments) to call to a dispatcher loop.

-- Barry
 
O

Ole Nielsby

Barry Kelly said:
[...some insights in generics compilation...]

So, I would say it is a current architectural limitation of the CLR.

Thanks for a detailed explanation.

I'll go for a combination of reflection and static fields. Using
reflection in the class initializer will be faster than cluttering
the constructors with factory parameters, I think.

My SharedPetLover<T> gets a static field that is filled in by
using reflection to get the SharedPet<T> singleton. Or,
expanding on your version:

class A<T>
{
public static T Value
{
get { return default(T); }
}
}

class B<T,U>
where T : A<U>
{
public U Value
{
get { return value; }
}
private static U value =
(U)typeof(U).InvokeMember(
"Value", flags, null, null, null);
const flags =
BindingFlags.GetProperty
| BindingFlags.Public
| BindingFlags.Static
| BindingFlags.FlattenHierarchy;
}


This may complicate the initialization process a bit,
but once it's done I get fast object creation. (I'm
still slightly puzzled that it can't be done without
reflection or factories, but I'll take your words for
that.)

Regards/Ole N.
 
B

Barry Kelly

Ole Nielsby said:
Barry Kelly said:
[...some insights in generics compilation...]

So, I would say it is a current architectural limitation of the CLR.

Thanks for a detailed explanation.

Belay that order! Your post got me thinking, and I realised that the
same "problem" of passing the generic argument would occur for instance
methods. So I investigated further with ILASM. I patched up the IL that
referred to an instance property to refer to a static property instead.
It compiled correctly with ilasm, and ran correctly.

I then ran it through Reflector to see what it thought of the IL. Then I
saw the error of my ways...

You can indeed access a static property of a type argument, via the more
prosaic route of using the same type as specified in the constraint:

---8<---
class A<T>
{
public static T StaticValue
{
get { return default(T); }
}
}

class B<T,U>
where T : A<U>
{
public B()
{
}

public U Value
{
get { return A<U>.StaticValue; }
}
}
--->8---

Don't forget that the storage for a static field is the shared for all
its descendants - there isn't a unique copy of statics per descendant
class, so the limitation on directly accessing T isn't actually a
limitation at all.

Sorry for overcomplicating matters...

HTH,

-- Barry
 
O

Ole Nielsby

Barry Kelly said:
Belay that order! Your post got me thinking, and I [...]
saw the error of my ways...

Out goes using System.Reflection; and my code suddenly
looks more readable.

At last I can go to sleep without feeling too stupid!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top