为什么C#中的计数器(使用CRTP)对某些类型的对象不计数?

时间:2015-11-10 06:03:17

标签: c# generics count visual-studio-2015 crtp

我已经使用CRTP在C#计数器中实现了,但我不知道为什么它不会因某些类型而倒计时:

using System;

using Type1 = C<char>;
using Type2 = C<int>;

class ExistingObjectCounter<CountedType>
{
    private static uint existingObjects = 0;

    protected ExistingObjectCounter()
    {
        ++existingObjects;
    }

    ~ExistingObjectCounter()
    {
        Console.WriteLine("Destruction of " + this + " number " + existingObjects);
        --existingObjects;
    }

    public static uint GetNumberOfLivingObjects
    {
        get
        {
            return existingObjects;
        }
    }
}

class C<T> : ExistingObjectCounter<C<T>>
{
    public C() 
    {}
    public C(C<T> m)
    {}
};

class ZliczaczObiektow
{
    public static void createManyCClassWithStringArgument()
    {
        for(int i=0; i<10;++i)
            new C<String>();
    }

    public static void Main()
    {
        Type1 c1 = new Type1(), c2 = new Type1(), c3 = new Type1(c2);
        var ws1  = new Type2();

        Console.WriteLine("existing objects of class " + c1.GetType() + ": "
                  + Type1.GetNumberOfLivingObjects);
        Console.WriteLine("existing objects of class " + ws1.GetType() + ": "
                  + Type2.GetNumberOfLivingObjects);

        createManyCClassWithStringArgument();

        {
            Type1 c4 = new Type1(), c5 = new Type1();
            var ws2  = new Type2();

            Console.WriteLine("existing objects of class " + c4.GetType() + ": "
                      + Type1.GetNumberOfLivingObjects);
            Console.WriteLine("existing objects of class " + ws2.GetType() + ": "
                      + Type2.GetNumberOfLivingObjects);

            createManyCClassWithStringArgument();
        }
        System.GC.Collect();

        Console.WriteLine("existing objects of class " + c1.GetType() + ": "
                  + Type1.GetNumberOfLivingObjects);
        Console.WriteLine("existing objects of class " + ws1.GetType() + ": "
                  + Type2.GetNumberOfLivingObjects);

        Console.WriteLine("existing objects of class C<String>:  "
          + C<String>.GetNumberOfLivingObjects);

        Console.ReadKey();
    }
}

输出是:

existing objects of class C`1[System.Char]:  3
existing objects of class C`1[System.Int32]: 1
existing objects of class C`1[System.Char]:  5
existing objects of class C`1[System.Int32]: 2
existing objects of class C`1[System.Char]:  5
existing objects of class C`1[System.Int32]: 2
existing objects of class C<String>:  20
Destruction of C`1[System.String] number 20
Destruction of C`1[System.String] number 19
Destruction of C`1[System.String] number 18
Destruction of C`1[System.String] number 17
Destruction of C`1[System.String] number 16
Destruction of C`1[System.String] number 15
Destruction of C`1[System.String] number 14
Destruction of C`1[System.String] number 13
Destruction of C`1[System.String] number 12
Destruction of C`1[System.String] number 11
Destruction of C`1[System.String] number 10
Destruction of C`1[System.String] number 9
Destruction of C`1[System.String] number 8
Destruction of C`1[System.String] number 7
Destruction of C`1[System.String] number 6
Destruction of C`1[System.String] number 5
Destruction of C`1[System.String] number 4
Destruction of C`1[System.String] number 3
Destruction of C`1[System.String] number 2
Destruction of C`1[System.String] number 1

编辑: 我的问题是为什么在某些情况下计数器不会下降,而在另一些情况下呢? 为什么Type1和Type2没有在程序结束时调用析构函数,但另一方面,使用String参数化的C调用了析构函数?

2 个答案:

答案 0 :(得分:1)

您不能依赖任何垃圾收集器行为。如果你需要析构函数类型的行为,那么让你的类继承IDispose并手动调用Dispose()。

答案 1 :(得分:0)

马克·费尔德曼对他的回答以及他的建议通常做了什么,而不是依赖于垃圾收集器(因为即使明确地称之为GC也太不可靠),因此非常好。

但是对于你的问题:你已经归结为垃圾收集器的主要问题。即使被调用,也无法保证从内存中删除所有不再使用(引用)的变量(对于需要完全不同于其他任何东西的图像,情况更糟)。因此,您正在观察的是GC正在完成其工作,但并不像您期望的那样。

这是你提出这两个问题的唯一原因:为什么有些对象计数会下降而有些则没有(尽管没有引用)。

您可以尝试的一件事是,在致电GC.Collect();后,您也会致电GC.WaitForPendingFinalizers();。这可以帮助但遗憾的是,与垃圾收集器一样,它不能100%保证它会有所帮助。

因此,如果您不是绝对有必要使用垃圾收集器(绝对是:如果不消除程序因此而崩溃的异常变量,则使用如此多的内存)那么你应该避免使用它例如Mark Feldman在他的回答中描述的方法。