#737 – When to Implement a Finalizer

In C#, you implement a finalizer using the destructor (~) syntax.  The finalizer is called by the system when the object is being destroyed.

In general, you rarely need to implement a finalizer.  Rules of thumb for implementing a finalizer include:

  • Implement a finalizer only when the object has unmanaged resources to clean up (e.g. file handles)
  • Do not implement a finalizer if you don’t have unmanaged resources to clean up
  • The finalizer should release all of the object’s unmanaged resources
  • Implement the finalizer as part of the dispose pattern, which allows for deterministic destruction
  • The finalizer should only concern itself with cleanup of objects owned within the class where it is defined
  • The finalizer should avoid side-effects and only include cleanup code
  • The finalizer should not add references to any objects, including a reference to the finalizer’s own object
  • The finalizer should not call methods in any other objects

#736 – Target of a WeakReference Will Be null after Garbage Collection

You can create a WeakReference object that refers to a regular reference-typed object and lets you discover whether the original object has been garbage collected or not.  The IsAlive property of the WeakReference can be used to determine whether the object has been garbage collected or not.  (Although a value of true can’t be trusted).

You can use the Target property of the WeakReference to reference the original object, if it’s still alive.  However, if the original object has already been garbage collected, the Target property will be null and you can then no longer reference the original object.

            Dog dog = new Dog("Bowser");

            WeakReference dogRef = new WeakReference(dog);

            // Bowser gets garbage collected
            dog = null;
            GC.Collect();

            Console.WriteLine(string.Format("Object still alive: {0}", dogRef.IsAlive));

            // dogRef.Target will now return null, since
            // original Dog object was garbage collected
            Dog origDog = (Dog)dogRef.Target;
            if (origDog != null)
                origDog.Bark();
            Console.WriteLine("Bowser is gone..");

736-001

#735 – Don’t Trust WeakReference.IsAlive If It Returns true

The WeakReference.IsAlive method can tell you if a particular object is still alive, i.e. it has not yet been garbage collected.  However, when IsAlive returns true, you can’t then necessarily interact with the object through its weak reference, because it could be garbage collected before you get a chance to use it.

For example:

            WeakReference dogRef = new WeakReference(dog);

            // Later, try to ref original Dog

            if (dogRef.IsAlive)
            {
                // Oops - garbage collection on original Dog could occur here
                ((Dog)dogRef.Target).Bark();
            }

You can trust IsAlive when it returns false, since a garbage collected object is not in danger of being reconstituted.  But the correct pattern for checking to see if an object is still alive is:

            WeakReference dogRef = new WeakReference(dog);

            // Later, try to ref original Dog

            Dog origDog = (Dog)dogRef.Target;
            if (origDog != null)
            {
                origDog.Bark();
            }

#734 – Accessing the Original Object Referenced through a WeakReference

You can create a WeakReference instance that refers to an object and then use the WeakReference to determine whether the object has been garbage collected.

You can also refer back to the original object referenced by the WeakReference using its Target property.

            Dog dog = new Dog("Bowser");

            WeakReference dogRef = new WeakReference(dog);

            Dog origDog = (Dog)dogRef.Target;

When you refer to the original object in this way, you add a reference to it, which means that it won’t get garbage collected.

            Dog dog = new Dog("Bowser");

            WeakReference dogRef = new WeakReference(dog);

            Dog origDog = (Dog)dogRef.Target;

            dog = null;
            GC.Collect();

            // Bowser still alive at this point, since we still have
            // a reference to him.

            Console.WriteLine(string.Format("Object still alive: {0}", dogRef.IsAlive));

734-001

#733 – How to Tell If an Object Has Been Garbage Collected

You normally can’t tell whether an object has been garbage collected by using some reference to the object–because once you have a reference to the object, it won’t be garbage collected.

You can instead create a weak reference to an object using the WeakReference object.  The weak reference is one that won’t be counted as a reference, for purposes of garbage collection.

In the code below, we check before and after garbage collection to show that a Dog object is garbage collected.

            Dog dog = new Dog("Bowser");

            WeakReference dogRef = new WeakReference(dog);
            Console.WriteLine(dogRef.IsAlive);

            dog = null;
            GC.Collect();

            Console.WriteLine(dogRef.IsAlive);

733-001

#732 – Destruction vs. Collection

Automatic memory management in C# means that the memory for a heap-based object is automatically released when there is no longer any user-written code that references the object.  This is done by the garbage collector.

An object goes through two distinct phases when it is no longer referenced–destruction and collection.

When an object is no longer referenced by any user-written code, other than code in a destructor, the object becomes eligible for destruction.

Once the object is eligible for destruction, it may be destructed at some point in the future, i.e. its destructor is called.

After the object is destructed and it is no longer being referenced by any code, including code in a destructor, the object becomes eligible for collection.

Once the object is eligible for collection, the garbage collector may at some point in the future release memory for the object.

#731 – Getting Information About the Members of a Class

You can use the Type.GetMembers method to get information about all public members in a type.  GetMembers returns an array of MemberInfo objects, each of which can be used to get details about one of the members of the type.

Here’s a sampling of some of the information that you can get:

            MemberInfo[] members = typeof(Dog).GetMembers();

            if (members != null)
                foreach (MemberInfo mi in members)
                {
                    Console.WriteLine(mi.Name);
                    Console.WriteLine(string.Format("  MemberType: {0}", mi.MemberType));

                    object[] attributes = mi.GetCustomAttributes(false);
                    if ((attributes != null) && (attributes.Length > 0))
                    {
                        Console.Write("  Attributes: ");
                        foreach (object attr in attributes)
                            Console.Write(string.Format("{0} ", attr.ToString()));
                        Console.WriteLine();
                    }

                    // Get subtype-specific information
                    switch (mi.MemberType)
                    {
                        case MemberTypes.Constructor:
                            DumpConstructorInfo((ConstructorInfo)mi);
                            break;

                        case MemberTypes.Event:
                            DumpEventInfo((EventInfo)mi);
                            break;

                        case MemberTypes.Method:
                            DumpMethodInfo((MethodInfo)mi);
                            break;
                    }

                    Console.WriteLine();
                }
        private static void DumpConstructorInfo(ConstructorInfo ci)
        {
            Console.WriteLine(string.Format("  Calling Convention: {0}", ci.CallingConvention));

            StringBuilder sbInfo = new StringBuilder("  ");
            if (ci.IsAbstract) sbInfo.Append("Abstract ");
            if (ci.IsGenericMethod) sbInfo.Append("GenericMethod ");
            if (ci.IsHideBySig) sbInfo.Append("HideBySig ");
            if (ci.IsPrivate) sbInfo.Append("Private ");
            if (ci.IsPublic) sbInfo.Append("Public ");
            if (ci.IsStatic) sbInfo.Append("Static ");
            if (ci.IsVirtual) sbInfo.Append("Virtual ");

            if (sbInfo.Length > 2)
                Console.WriteLine(sbInfo);
        }

        private static void DumpEventInfo(EventInfo ei)
        {
            Console.WriteLine(string.Format("  Event Handler Type: {0}", ei.EventHandlerType.Name));
            if (ei.IsMulticast) Console.WriteLine("  IsMulticast");
        }

        private static void DumpMethodInfo(MethodInfo mi)
        {
            Console.WriteLine(string.Format("  Calling Convention: {0}", mi.CallingConvention));

            StringBuilder sbInfo = new StringBuilder("  ");
            if (mi.IsAbstract) sbInfo.Append("Abstract ");
            if (mi.IsGenericMethod) sbInfo.Append("GenericMethod ");
            if (mi.IsHideBySig) sbInfo.Append("HideBySig ");
            if (mi.IsPrivate) sbInfo.Append("Private ");
            if (mi.IsPublic) sbInfo.Append("Public ");
            if (mi.IsStatic) sbInfo.Append("Static ");
            if (mi.IsVirtual) sbInfo.Append("Virtual ");

            ParameterInfo[] paraminfo = mi.GetParameters();
            if ((paraminfo != null) && (paraminfo.Length > 0))
            {
                Console.Write("  Parameters: ");
                foreach (ParameterInfo pi in paraminfo)
                {
                    Console.Write(string.Format("{0} ({1})  ", pi.Name, pi.ParameterType.Name));
                }
                Console.WriteLine();
            }
        }

731-001

#730 – Check for null Before Iterating Using foreach Statement

The foreach statement allows iterating through all items in a collection.

            List<string> dogs = new List<string>();
            dogs.Add("Kirby");
            dogs.Add("Jack");

            foreach (string s in dogs)
                Console.WriteLine(s);

            Console.WriteLine("=> Done");

730-001

If the collection referenced in the foreach statement is empty, the body of the loop does not execute.  No exception is thrown.

            List<string> dogs = new List<string>();

            foreach (string s in dogs)
                Console.WriteLine(s);

            Console.WriteLine("=> Done");

730-002
If the object that refers to the collection is null, a NullReferenceException is thrown.

            List<string> dogs = null;

            foreach (string s in dogs)
                Console.WriteLine(s);

            Console.WriteLine("=> Done");

730-003
Because of this, it’s good practice to check for null before iterating through a collection in a foreach loop.

            if (dogs != null)
                foreach (string s in dogs)
                    Console.WriteLine(s);

#729 – Dumping Out All Types in .NET Framework, part II

Here’s an updated copy of the code that iterates through all .NET assemblies, dumping out type information.  This code iterates through all .NET assemblies and dumps out a master list of all types, organized by namespace.  Information about the type members is dumped out for each type and everything is written to a file.  The code has also been updated to descend into all sub-directories of the .NET runtime directory.

When I run this against .NET 4.0.30319, I get a total of 50,380 types, in 872 namespaces.

    class Program
    {
        static void Main()
        {
            int totalNamespaces = 0;
            int totalTypes = 0;

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;

            SortedList<string, SortedList<string, Type>> fullList =
                new SortedList<string, SortedList<string, Type>>();

            DirectoryInfo di = new DirectoryInfo(RuntimeEnvironment.GetRuntimeDirectory());
            foreach (FileInfo fi in di.GetFiles("*",SearchOption.AllDirectories))
            {
                Trace.WriteLine("File: {0}", fi.FullName);
                try
                {
                    Assembly assem = Assembly.LoadFrom(fi.FullName);

                    if (assem != null)
                    {
                        ExtractListOfTypes(assem, ref fullList, ref totalNamespaces, ref totalTypes);
                    }
                }
                catch (Exception xx)
                {
                    Trace.WriteLine(string.Format("EXCEPTION on {0}: {1}", fi.FullName, xx.ToString()));
                }
            }

            DumpTypeList(fullList, totalNamespaces, totalTypes);
        }

        static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        private static void ExtractListOfTypes(Assembly assem, ref SortedList<string, SortedList<string, Type>> theList,
            ref int totalNamespaces, ref int totalTypes)
        {
            if (theList == null)
                throw new Exception("Uninitialized list");

            foreach (Type t in assem.GetTypes())
            {
                string theNamespace = t.Namespace != null ? t.Namespace : "(global)";

                // Add namespace if it's not already in list
                if (!theList.ContainsKey(theNamespace))
                {
                    theList.Add(theNamespace, new SortedList<string, Type>());
                    totalNamespaces++;
                }

                // And add type under appropriate namespace
                if (!theList[theNamespace].ContainsKey(t.FullName))
                {
                    theList[theNamespace].Add(t.FullName, t);
                    totalTypes++;
                }
            }

            return;
        }

        private static void DumpTypeList(SortedList<string, SortedList<string, Type>> theList,
                                         int totalNamespaces, int totalTypes)
        {
            using (StreamWriter writer = new StreamWriter(@"C:\Test\AllTypes.txt"))
            {
                writer.WriteLine(string.Format("Total Namespaces: {0}", totalNamespaces));
                writer.WriteLine(string.Format("Total Types: {0}", totalTypes));

                foreach (KeyValuePair<string, SortedList<string, Type>> kvp in theList)
                {
                    writer.WriteLine(string.Format("Namespace: [{0}]", kvp.Key));

                    foreach (KeyValuePair<string, Type> kvpInner in kvp.Value)
                    {
                        Type t = (Type)kvpInner.Value;
                        writer.WriteLine(string.Format("  Type: [{0}] ({1})", t.FullName, TypeIndicator(t)));
                        DumpTypeInfo(t, writer);
                    }

                    writer.WriteLine();
                }
            }
        }

        private static void DumpTypeInfo(Type t, StreamWriter writer)
        {
            writer.WriteLine(string.Format("    Assembly: {0}", t.Assembly.FullName));
            if (t.BaseType != null)
                writer.WriteLine(string.Format("    Base Type: {0}", t.BaseType.FullName));

            MemberInfo[] members = t.GetMembers();
            if (members.Length > 0)
            {
                writer.WriteLine("    Members:");
                foreach (MemberInfo mi in members)
                {
                    writer.WriteLine(string.Format("      {0}  ({1})", mi.ToString(), mi.MemberType));
                }
            }
        }

        private static string TypeIndicator(Type t)
        {
            string typeIndicator = "?";

            if ((t.BaseType != null) &&
                (t.BaseType.FullName == "System.MulticastDelegate"))
                typeIndicator = "delegate";

            else if (t.IsClass)
            {
                if (t.IsNested)
                    typeIndicator = "Nested class";
                else
                    typeIndicator = "class";
            }
            else if (t.IsInterface)
                typeIndicator = "interface";

            else if (t.IsValueType)
                typeIndicator = "struct";

            else if (t.IsEnum)
                typeIndicator = "enum";

            return typeIndicator;
        }
    }

#728 – Dumping Out All Types in the .NET Framework

Here’s some code that looks through all assemblies in the .NET Framework directory and uses to reflection to dump out all of the types, organized by namespace.

        static void Main()
        {
            int totalNamespaces = 0;
            int totalTypes = 0;

            AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += CurrentDomain_ReflectionOnlyAssemblyResolve;

            SortedList<string, SortedList<string, Type>> fullList =
                new SortedList<string, SortedList<string, Type>>();

            DirectoryInfo di = new DirectoryInfo(RuntimeEnvironment.GetRuntimeDirectory());
            foreach (FileInfo fi in di.GetFiles())
            {
                try
                {
                    Assembly assem = Assembly.LoadFrom(fi.FullName);

                    if (assem != null)
                    {
                        ExtractListOfTypes(assem, ref fullList, ref totalNamespaces, ref totalTypes);
                    }
                }
                catch { }
            }

            Console.WriteLine(string.Format("{0} types, in {1} namespaces", totalTypes, totalNamespaces));
            DumpTypeList(fullList);
        }

        static Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
        {
            return Assembly.ReflectionOnlyLoad(args.Name);
        }

        private static void ExtractListOfTypes(Assembly assem, ref SortedList<string, SortedList<string, Type>> theList,
            ref int totalNamespaces, ref int totalTypes)
        {
            if (theList == null)
                throw new Exception("Uninitialized list");

            foreach (Type t in assem.GetTypes())
            {
                string theNamespace = t.Namespace != null ? t.Namespace : "(global)";

                // Add namespace if it's not already in list
                if (!theList.ContainsKey(theNamespace))
                {
                    theList.Add(theNamespace, new SortedList<string, Type>());
                    totalNamespaces++;
                }

                // And add type under appropriate namespace
                if (!theList[theNamespace].ContainsKey(t.FullName))
                {
                    theList[theNamespace].Add(t.FullName, t);
                    totalTypes++;
                }
            }

            return;
        }

        private static void DumpTypeList(SortedList<string, SortedList<string, Type>> theList)
        {
            foreach (KeyValuePair<string,SortedList<string,Type>> kvp in theList)
            {
                Console.WriteLine(string.Format("Namespace: [{0}]", kvp.Key));

                foreach (KeyValuePair<string, Type> kvpInner in kvp.Value)
                {
                    Console.WriteLine(string.Format("  Type: [{0}]", ((Type)kvpInner.Value).FullName));
                }

                Console.WriteLine();
            }
        }

When I run this against .NET 4.0.30319, I get a total of 40,166 types, in 657 namespaces.

728-001