Seriuos monkey business with static generics

O

Ole Nielsby

I'm puzzled at how static fields intersect with generics, as the
following snippet illustrates.


namespace MonkeyParty
{
abstract class Fruit { }
class Apple : Fruit { }
class Pear : Fruit { }

abstract class Tree<TFruit, TTree>
where TFruit : Fruit, new()
where TTree : Tree<TFruit, TTree>, new()
{
public static TTree TheTree = new TTree();
public TFruit shake() { return new TFruit(); }
}

sealed class Monkey<TFruit> where TFruit : Fruit, new()
{
private sealed class MyFavoriteTree : Tree<TFruit, MyFavoriteTree>
{ }
public TFruit GetFood() { return MyFavoriteTree.TheTree.shake(); }
public void Eat(TFruit food) { }
}

static class Garden
{
static void MonkeyParty ()
{
Monkey<Apple> Albert = new Monkey<Apple>();
Monkey<Apple> Arnold = new Monkey<Apple>();
Monkey<Pear> Perry = new Monkey<Pear>();

Albert.Eat(Arnold.GetFood());
Arnold.Eat(Albert.GetFood());
// Perry.Eat(Arnold.GetFood()); --- compile time error ---
Perry.Eat(Perry.GetFood());
}
}
}


The setup is as follows:

There are two kinds of fruits, each of which grow on a single tree.

So the trees are singletons, and as such, I placed them as
static members of their own class. To allow this, I need a
circular spec, but this seems allright with the compiler.

Then, there are three monkeys, Albert, Arnold and Perry.
Albert and Arnold like apples, Perry likes pears. If you ask
an abe to fetch a fruit, it will go to and shake the tree of its
favourite fruit. Albert and Arnold can feed each other, while
Perry needs to fetch his own fruit and eat it.

Now the docs state that there is only one Tree class -
I quote:

"Generics work a little differently for reference types. The
first time a generic type is constructed with any reference
type, the runtime creates a specialized generic type with
object references substituted for the parameters in the MSIL.
Then, each time a constructed type is instantiated with a
reference type as its parameter, regardless of what type it is,
the runtime reuses the previously created specialized version
of the generic type. This is possible because all references are
the same size."

But if there is only one Tree class, how can it have two differently
typed instances of the same static variable? I mean, can I be
certain that Perry won't unwittingly get an apple because Albert
happened to plant an apple tree in the static variable?

(In case anyone wonders why I made this up: it's a toy model of
an attempt to construct a data model with uniquely represented
constants of various type. The fruits are wrappers for the
constant types, the trees are collections that serve to unify the
fruits, and the monkeys are weak pointer based wrappers for
the fruits (they need to be weakly referenced to allow garbage
collection of non-refernced constants). This is not an accurate
mapping but should give some idea of what I'm trying to do with
statics and generics. Each constant type has its own tree, which
makes the unification simpler.)

Regards/Ole Nielsby
 
L

Larry Lard

Ole said:
I'm puzzled at how static fields intersect with generics, as the
following snippet illustrates.


namespace MonkeyParty
{
abstract class Fruit { }
class Apple : Fruit { }
class Pear : Fruit { }

abstract class Tree<TFruit, TTree>
where TFruit : Fruit, new()
where TTree : Tree<TFruit, TTree>, new()
{
public static TTree TheTree = new TTree();
public TFruit shake() { return new TFruit(); }
}

sealed class Monkey<TFruit> where TFruit : Fruit, new()
{
private sealed class MyFavoriteTree : Tree<TFruit, MyFavoriteTree>
{ }
public TFruit GetFood() { return MyFavoriteTree.TheTree.shake(); }
public void Eat(TFruit food) { }
}

static class Garden
{
static void MonkeyParty ()
{
Monkey<Apple> Albert = new Monkey<Apple>();
Monkey<Apple> Arnold = new Monkey<Apple>();
Monkey<Pear> Perry = new Monkey<Pear>();

Albert.Eat(Arnold.GetFood());
Arnold.Eat(Albert.GetFood());
// Perry.Eat(Arnold.GetFood()); --- compile time error ---
Perry.Eat(Perry.GetFood());
}
}
}


The setup is as follows:

There are two kinds of fruits, each of which grow on a single tree.

So the trees are singletons, and as such, I placed them as
static members of their own class. To allow this, I need a
circular spec, but this seems allright with the compiler.

My gut instinct is that this is all wrong, but I don't know enough to
be sure. I will leave this for the experts :)

Now the docs state that there is only one Tree class -
I quote:

"Generics work a little differently for reference types. The
first time a generic type is constructed with any reference
type, the runtime creates a specialized generic type with
object references substituted for the parameters in the MSIL.
Then, each time a constructed type is instantiated with a
reference type as its parameter, regardless of what type it is,
the runtime reuses the previously created specialized version
of the generic type. This is possible because all references are
the same size."

But if there is only one Tree class, how can it have two differently
typed instances of the same static variable? I mean, can I be
certain that Perry won't unwittingly get an apple because Albert
happened to plant an apple tree in the static variable?

Different levels. There is only one Tree class at an internal *code*
level; but at the *conceptual* level, Tree<Apple> and Tree<Pear> are
still different classes. A simpler example makes things clear:

class Generic<T>
where T : class
{
public static int i;
}

class Program
{
static void Main(string[] args)
{
Generic<String>.i = 3;

Console.WriteLine(Generic<String>.i);
Console.WriteLine(Generic<Array>.i);

Console.ReadLine();
}
}

}

Generic<Array> is a different class from (even though, since T is a
reference type, it will share a code implementation with)
Generic<String>, so we get 3 and 0 output.
 

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