tags:

views:

175

answers:

3

Here is my situation. I am working with WMI in C#. Thankfully, I found MgmtClassGen.exe which generated all of the classes I need for now.

However, I have been going through the auto generated classes, and ripping out the common code to either a Utility class, or a base class. So far so good, got a lot of the code cleaned up. But I've hit a snag. Each class has several (about 8) static functions called GetInstances. Basically there are 2 overloads, and the other function are just providing default parameters.

I would like to put these function in the base class because they are identical across all the classes, except for 3 variables. Namely, the ClassName (like "MicrosoftDNS_Zone") of the object, the Namespace (like "root\microsoftdns") of the Object, and a ManagementScope object.

What I have done currently is this; Moved the 2 functions with code in them to the base class and added 3 parameters, for the 3 differences listed above. But this still requires 8 functions in each class that just call the base class with the ClassName, Namespace, and Scope parameters filled in.

  1. Is there any way to refactor this code so that I don't need the method "wrappers" in each derived class?
  2. Can I just declare the static methods in the base class and somehow get the ClassName, etc. from the derived class?
  3. Would reflection even work here?

Base Class:

namespace WMI
{
    public abstract class Object : System.ComponentModel.Component
    {
        // ...
        protected static WMI.ManagementTypeCollection GetInstances( string className, string namespaceName, ManagementScope statMgmtScope, ManagementScope mgmtScope, EnumerationOptions enumOptions, Func<ManagementObject,WMI.Object> del )
        {
            if( (mgmtScope == null) )
            {
                if( (statMgmtScope == null) )
                {
                    mgmtScope = new System.Management.ManagementScope();
                    mgmtScope.Path.NamespacePath = namespaceName;
                }
                else
                {
                    mgmtScope = statMgmtScope;
                }
            }
            System.Management.ManagementPath pathObj = new System.Management.ManagementPath();
            pathObj.ClassName = className;
            pathObj.NamespacePath = namespaceName;
            System.Management.ManagementClass clsObject = new System.Management.ManagementClass( mgmtScope, pathObj, null );
            if( (enumOptions == null) )
            {
                enumOptions = new System.Management.EnumerationOptions();
                enumOptions.EnsureLocatable = true;
            }
            return new WMI.ManagementTypeCollection( clsObject.GetInstances( enumOptions ), del );
        }

        protected static WMI.ManagementTypeCollection GetInstances( string className, string namespaceName, ManagementScope statMgmtScope, ManagementScope mgmtScope, string condition, String[] selectedProperties, Func<ManagementObject, WMI.Object> del )
        {
            if( (mgmtScope == null) )
            {
                if( (statMgmtScope == null) )
                {
                    mgmtScope = new System.Management.ManagementScope();
                    mgmtScope.Path.NamespacePath = namespaceName;
                }
                else
                {
                    mgmtScope = statMgmtScope;
                }
            }
            System.Management.ManagementObjectSearcher ObjectSearcher = new System.Management.ManagementObjectSearcher( mgmtScope, new SelectQuery( className, condition, selectedProperties ) );
            System.Management.EnumerationOptions enumOptions = new System.Management.EnumerationOptions();
            enumOptions.EnsureLocatable = true;
            ObjectSearcher.Options = enumOptions;
            return new WMI.ManagementTypeCollection( ObjectSearcher.Get(), del );
        }
    }
}

Derived Class:

namespace WMI.MicrosoftDNS
{
    public class AAAAType : WMI.Object
    {
        private static string CreatedWmiNamespace = "root\\microsoftdns";
        private static string CreatedClassName = "MicrosoftDNS_AAAAType";
        private static System.Management.ManagementScope statMgmtScope = null;

        // ...

        public static WMI.ManagementTypeCollection GetInstances()
        {
            return GetInstances( null, null, null );
        }

        public static WMI.ManagementTypeCollection GetInstances( string condition )
        {
            return GetInstances( null, condition, null );
        }

        public static WMI.ManagementTypeCollection GetInstances( System.String[] selectedProperties )
        {
            return GetInstances( null, null, selectedProperties );
        }

        public static WMI.ManagementTypeCollection GetInstances( string condition, System.String[] selectedProperties )
        {
            return GetInstances( null, condition, selectedProperties );
        }

        public static WMI.ManagementTypeCollection GetInstances( ManagementScope mgmtScope, EnumerationOptions enumOptions )
        {
            return WMI.Object.GetInstances( CreatedClassName, CreatedWmiNamespace, statMgmtScope, mgmtScope, enumOptions, mo => new AAAAType( mo ) );
        }

        public static WMI.ManagementTypeCollection GetInstances( ManagementScope mgmtScope, string condition )
        {
            return GetInstances( mgmtScope, condition, null );
        }

        public static WMI.ManagementTypeCollection GetInstances( ManagementScope mgmtScope, System.String[] selectedProperties )
        {
            return GetInstances( mgmtScope, null, selectedProperties );
        }

        public static WMI.ManagementTypeCollection GetInstances( ManagementScope mgmtScope, string condition, System.String[] selectedProperties )
        {
            return WMI.Object.GetInstances( CreatedClassName, CreatedWmiNamespace, statMgmtScope, mgmtScope, condition, selectedProperties, mo => new AAAAType( mo ) );
        }
    }
}
+1  A: 

It might not be an option for you, but C# 4.0 supports optional arguments with default values for methods, which removes the need to have multiple overloads in a lot of cases. For example:

public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10)

You could call the above method with ExampleMethod(1) or ExampleMethod(1, 'Test', 9).

tbreffni
Yes, I am using C# 4.0, and plan on doing this. However, that see leaves me needing to copy/manipulate 2 functions (there are 2 differents overload there really) in close to 75 files. Plus more if I need to work with any other WMI classes... And I always hate having duplicate code anywhere, espicially in 75+ files :-\
TJMonk15
Maybe you could use custom attributes and reflection to set and read the unique values per class? http://msdn.microsoft.com/en-us/library/z0w1kczw%28VS.100%29.aspx
tbreffni
Will that work correctly in a static context from the base class?
TJMonk15
+2  A: 

Why not move these eight common functions into a separate class as instance methods, and initialize the instance with the three parameters that vary by class. Then you could aggregate and expose the instance of that helper class on each of the derived types. Since the static methods can't use any instance data, there's no reason to duplicate them.

Here's an example of what I mean:

public class WMIInstance
{
    private readonly string CreatedWmiNamespace = "root\\microsoftdns";
    private readonly string CreatedClassName = "MicrosoftDNS_AAAAType";
    private readonly System.Management.ManagementScope statMgmtScope = null;

    public WMIInstance( string namespace, string className, ManagementScope scope )
    { /*... initialize private members here... */

    public WMI.ManagementTypeCollection GetInstances( System.String[] selectedProperties )
    { return WMI.Object.GetInstances( ... ); }

    /* other overloads ... */
}

public class AAAAType : WMI.Object
{
    private static string CreatedWmiNamespace = "root\\microsoftdns";
    private static string CreatedClassName = "MicrosoftDNS_AAAAType";
    private static System.Management.ManagementScope statMgmtScope = null;

    private static readonly WMIInstances _instances = new WMIInstance( CreatedWmiNamespace, CreatedClassName, statMgmgtScope );

    public static WMIInstances Getter { get { return _instances; } }
}
LBushkin
Explain... How would that allow me to have the code only in the base class? (Which is my desired result...) ?
TJMonk15
I added an example. Basically, `AAAAType` gets a static members which provides access to the `GetInstances()` methods. These methods, then, in turn, become instance methods of the utility class `WMIInstance`. This way you instantiate `WMIInstance` for each derived WMI class and simply initialize it with the necessary parameters.
LBushkin
Note that the methods of `WMIInstance` are NOT static, but the created instance of `WMIInstance` is a static members of `AAAAType`.
LBushkin
While that would work, it is still a lot of duplicate code in every derived class. hmm...
TJMonk15
There's much less duplication in the derived classes. You are moving all of the code into a shared class - and creating one instance of the shared class in each derived class. There's a little bit of code that goes into each class - but this is a necessary evil, since in C# you *don't* have anything like a virtual static method.
LBushkin
A: 

Maybe a small factory?

class Config { string ClassName; string Namespace; ManagementScope Scope; }

static class Factory {
     public static readonly Dictionary<Type, Config> Configs = new ...;

     static GetInstances(Type requestedType, ...) {
         var config = Configs[requestedType];
         // work with it...
     }
}

class AAAAType {
     static AAAAType{
          Factory.Configs.Add(typeof(AAAAType), new Config{ ... });
     }
}

It may not be perfect code, but I didn't want to go far from your initial proposal. Moreover, I don't really like it and would recommend a redesign of your idea, because of:

  • too much static
  • 6 parameters to a method, including a function pointer Really?
  • System.String ? there could be other string-types so that you qualify it that strong?
  • a couple of usings at the top would help to avoid qualified names
  • getting intent of the code is not easy
This is mostly Auto-Generated code from a Microsoft Tool ~rolls eyes~ yea, it doesn't make a lot of sense. I'm trying to clean it up, hence this question. :)
TJMonk15