1

鉴于泛型类型为每个类型组合创建单独的静态字段实例,如果我想在所有类型中都有一个静态字段,这是一个有效的模式吗?

public class BaseClass
{
    public static int P = 0;
}

public class ChildClass<T> : BaseClass
{
    public static int Q = 0;

    public void Inc()
    {
        // ChildClass<int> will have a different "Q" than ChildClass<double> 
        Interlocked.Increment(ref Q); 
        // all types of ChildClass will increment the same P
        Interlocked.Increment(ref P); 
    }
}

这种方法有什么不安全的地方吗?我的玩具示例有效,但我只是想确保没有可怕的副作用、线程后果等 :)

4

2 回答 2

4

您可以使用Interlocked.Increment获得更多线程安全的代码。

public void Inc()
{
    Interlocked.Increment(ref Q); // ChildClass<int> will have a different "Q" than ChildClass<double> 
    Interlocked.Increment(ref P); // all types of ChildClass will increment the same P
}

或普通的旧lock

public class BaseClass
{
    protected static int P = 0;
    protected static object pLock = new object();
}

public class ChildClass<T> : BaseClass
{
    private static int Q = 0;
    private static object qLock = new object();

    public void Inc()
    {
        lock(qLock)
        {
            qLock++;
        }

        lock(pLock)
        {
            qLock++;
        }
    }
}

请注意,每一个T都会有不同的ChildClass<T>.Q,但永远只有一个BaseClass.P。这意味着您必须使用单独的锁定对象来处理Qand P(从技术上讲,您用来锁定的任何东西P也可以用来锁定 all Q,但这可能不是您想要做的)。

于 2013-03-21T23:55:32.200 回答
2

您的模式是有效的并且没有问题。没有静态继承之类的东西,但是您可以像往常一样访问任何类型的静态成员(前提是它们对您的派生类可见,例如不可见private):

BaseClass.P = 10;
ChildClass<string>.Q = 20;

C# 规范指出,对 32 位整数(即int)的单次读取或写入是原子的,这意味着它们可以在一条指令内完成,并且永远不会对另一个线程可见半写变量。

12.5 变量引用的原子性

以下数据类型的读取和写入应是原子的:bool、char、byte、sbyte、short、ushort、uint、int、float 和引用类型。此外,具有上一个列表中的基础类型的枚举类型的读取和写入也应该是原子的。其他类型的读取和写入,包括 long、ulong、double 和 decimal,以及用户定义的类型,不必是原子的。

当然,不能保证读写是原子的。例如,增加一个变量的值需要先读取然后再写入,这就是Interlocked方法发挥作用的地方。

顺便说一句,您可能知道您只会获得一个P静态变量,但Q静态变量的数量与您的 type 的不同泛型实例一样多ChildClass<T>

于 2013-03-22T00:02:27.603 回答