开发者

Need something like static inheritance in C#

I'm having a small design issue and wanted to consult. Lets say we have the following class hierarchy:

abstract class A
{
}

class B : A
{
}

class C: A
{
}

I want that both B and C have a certain field x so that it's value is different between the classes but shared among all instances of the same class (i.e: if b1, b2 are instances of B and c1,c2 instances of C then b1.x = b2.x and c1.x = c2.x and b1.x != c1.x). Is there an elegant way to do this by taking advantage of the fact that both B, C derive from the same base class or do I have to create a static field x in bot开发者_开发知识库h classes?

Thanks in advance.


You mean like this?

abstract class A
{
    static Dictionary<Type, int> all_x;
    protected int X {
        get { return all_x[GetType()]; }
        set { all_x[GetType()] = value; }
    }
}

If it has to be a field so you can pass by reference:

abstract class A
{
    class SharedType { int x; }
    static Dictionary<Type, SharedType> all_shared;
    protected SharedType Shared {
        get
        {
            Type t = GetType();
            SharedType result;
            if (!all_shared.TryGetValue(t, out result) {
                 result = new SharedType();
                 all_shared.Add(t, result);
            }
            return result;
        }
    }
}

Also, we can improve performance by doing the lookup only once per instance:

abstract class A
{
    class SharedType { int x; }
    static Dictionary<Type, SharedType> all_shared;
    protected SharedType Shared;
    A() {
        Type t = GetType();
        if (!all_shared.TryGetValue(t, out Shared) {
             Shared = new SharedType();
             all_shared.Add(t, Shared);
        }
    }
}


What should those values be for the field x? If you need to specify that the value of x for A should be "a", the value of x for B should be "b" etc., then you will have to specify the values "a", "b", ... somewhere and then you mught as well just use:

abstract class A {
    public static int x = 1;    // Just using "int" as example.
}

class B : A {
    public static int x = 2;
}

If you do not care what the values are (which type do you need then) but merely want the values to be "different", then instead of using fields you could use something like:

abstract class A {
    public int X { get { return this.GetType().GetHashCode(); } }
}

This does not take hash collisions into account, but maybe it is useful anyway?

What is it you are trying to achieve?


To build on Ben Voigt's first answer, I think what you want for your base class is this:

public abstract class A
    {
        private static ConcurrentDictionary<Type, int> _typeIDs = new ConcurrentDictionary<Type, int>();
        private static int _nextID = 1;

        public int TypeID
        {
            get
            {
                return _typeIDs.GetOrAdd(this.GetType(), type => System.Threading.Interlocked.Increment(ref _nextID));
            }
        }
    }


public abstract class A
{
    public abstract int Value { get; }
}

public class B : A
{
    public override int Value { get { return 1; } }
}

public class C : A
{
    public override int Value { get { return 2; } }
}


The only way I know to do this is if you make class A a generic class, i.e. class A<T>. Then have class B implement a different type for the generic type than the generic type that Class C implements.

If you don't use generics, then I believe this is impossible in .NET.

Here is an example where lets say the value you were interested in was a data structure with members int Foo and string Bar. One derive class could implement the an identical structure (but different derived type) than the other - the two structures would implement the same interface.

interface IAvalue
    {
        int Foo { get; set;}
        string Bar {get; set;}
    }

    struct BValue
        : IAvalue
    {

        public int Foo { get; set; }
        public string Bar { get; set; }
    }

    struct CValue
        : IAvalue
    {
        public int Foo { get; set; }
        public string Bar { get; set; }


    }

    abstract class A<T> where T : IAvalue
    {
        protected static T myValue;
    }

    class B : A<BValue>
    {
        static B()
        {
            myValue.Foo = 1;
            myValue.Bar = "text1";
        }
    }

    class C : A<CValue>
    {
        static C()
        {
            myValue.Foo = 2;
            myValue.Bar = "text2";
        }
    }


You can use one .net feature: If you have static data members in a generic class, .net creates different instances of static data members for each generic type you use.

So, you can write:

public abstract class A<T> where T : A<T>
{
    protected static int myVariable { get; set; }
}

And inherit your classes as:

public class B : A<B>
{
    public B()
    {
        myVariable = 1;
    }
    public int GetVariable()
    {
        return myVariable;
    }
}

public class C : A<C>
{
    public C()
    {
        myVariable = 2;
    }
    public int GetVariable()
    {
        return myVariable;
    }
}

Then every instance of B will have shared access to one instance of myVariable and every instance of C will have shared access to another.

So, if you add Set(int a) method:

public void Set(int a)
{
    myVariable = a;
}

And run the following code:

static void Main(string[] args)
{
    B b1 = new B();
    C c1 = new C();
    B b2 = new B();
    C c2 = new C();

    Console.Write("{0}; ", b1.GetVariable());  // 1
    Console.Write("{0}; ", b2.GetVariable());  // 1
    Console.Write("{0}; ", c1.GetVariable());  // 2
    Console.Write("{0}; ", c2.GetVariable());  // 2

    Console.WriteLine();

    c2.Set(333);

    Console.Write("{0}; ", b1.GetVariable());  // 1
    Console.Write("{0}; ", b2.GetVariable());  // 1
    Console.Write("{0}; ", c1.GetVariable());  // 333
    Console.Write("{0}; ", c2.GetVariable());  // 333

    Console.ReadLine();
}

You get: 1; 1; 2; 2; 1; 1; 333; 333; output.


I would suggest defining a static Dictionary<Type, Integer[]>, and having the base-class constructor call GetType() on itself and see if it's yet in the static dictionary. If not, create a new single-element array and store it in the dictionary. Otherwise grab the array from the dictionary and store it in an instance field. Then define a property which reads or writes element zero of the array. This approach will achieve the requested semantics for all derivatives and sub-derivatives of the class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜