1

The current structure of my classes goes a little bit like this: PC.Processor.Architecture[0] = The Architecture of the first processor (assuming a multi-processor system).

How I ideally want it is more like this: PC.Processor[0].Architecture, because it's a bit more self explanatory this way.

Is there a fairly efficient way of doing this with what I have? Bear in mind there are like, over 9000 properties in each of the classes Processor, Motherboard, Memory etc and WMI calls are not CPU-cheap to run.

Here are the important snippets for my classes

class PC
{
    public Processor Processor;
    public Motherboard Motherboard;

    // Constructor
    public PC()
    {
        Processor = new Processor();
        Motherboard = new Motherboard();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        Processor.GetInfo();
        Motherboard.GetInfo();
    }
}

class Processor
{
    public string[] Architecture;
    public string[] Availability;
    public UInt16[] Cores;

    public void GetInfo()
    {
        // Get WMI Information from custom process
        // Returns as an array of ManagementObjects for each matched device (which is a bit like an array of dictionaries)
        ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_Processor");
        try
        {
            for (int i = 1; i < WMIData.Length; i++)
            {
                this.Architecture[i] = (string)WMIData[i]["Architecture"];
                this.Availability[i] = (string)WMIData[i]["Availability"];
                this.Cores[i] = (UInt16)WMIData[i]["NumberOfCores"];
            }
        }
        catch (NullReferenceException e)
        {
            // To be implemented
        }
    }
}

FURTHERMORE
There may be more than one WMI search query per class. For example, HardDrive needs to make use of both Win32_PhysicalMedia and ATAPI_SmartData (or whatever the class actually is.)

7
  • 1
    So one instance of the Processor class is used to represent N processors? That seems like a design problem. Care to explain the reasoning for that? Why not have Processor represent a single processor? Commented Mar 6, 2012 at 21:21
  • 2
    @sll, what are you talking about? First of all, List<T> is backed by an array. Second of all, boxing/unboxing doesn't occur with reference types. Commented Mar 6, 2012 at 21:22
  • Your PC class should have a collection of Processor instances, perhaps a member called Processors (with the s). Populate your collection of processors within PC. This way you can achieve what you want: PC.Processors[0].Cores Commented Mar 6, 2012 at 21:31
  • @servy This is a program to retrieve information about the computer it runs on. Currently, yes, one instance does list all physical processors. How I want it is an array of Processor instances each describe one processor. Commented Mar 6, 2012 at 21:34
  • @Tung I did think of doing that, but that would make the PC class massive and make obsolete the purpose of this segmented design. Commented Mar 6, 2012 at 21:35

3 Answers 3

2

You should probably be making these calls lazily, e.g.:

class PC
{
    public IEnumerable<Processor> Processors {get; private set;}

    // Constructor
    public PC()
    {
        Processors = new List<Processor>();
        for (var i = 0; i < GetProcessorCount(); i++)
            Processors.Add(new Processor(i));
    }
}

class Processor
{
    public int ProcessorIndex { get; private set; }

    private String _architecture;
    public string Architecture { 
        get { 
            // Return architecture if it's already been retrieved, 
            // otherwise retrieve it, store it, and return it.
            return _architecture ?? (_architecture == getArchitecture(ProcessorIndex));
        }
    }
    public Processor(int processorIndex) {
        ProcessorIndex = processorIndex;
    }
}

This way you get the semantics you want, e.g.

Console.Out.WriteLine(myPCInstance.Processors[0].Architecture);

and at the same time you are only retrieving that information when it is actually asked for.

Sign up to request clarification or add additional context in comments.

7 Comments

I may sound like an idiot, but using a get{} call for each and every property would result in unreadably massive code. Is this adaptable by just assigning values to properties?
Why would it be unreadable? You are going to need thousands of something for thousands of properties. Proper use of further nested classes, alphabetical order and perhaps regions should make this bearable, but short of sticking everything into a dictionary and using strings to get at it you are just going to have a lot of code here for thousands of properties.
500 properties x 1 line each = 500 lines of code to look through; 500 properties x 5 lines each = 2500 lines of code to look through.
As opposed to what? I am confused by what alternative you could possibly be looking for.
Why use lazy gets for each property? I don't understand. All I see it does it take up more paper when I print this all out for my coursework moderator to mark. The processor intensive bit is the GetWMIData function.
|
1

why not make a Processor class just have single properties Architecture, Cores etc and take a ManagementObject instance in its constructor? you can then pull the required data from the management object in the constructor of the Processor and just create many Processors in your PC object.

class PC
{
    //I'd encapsulate these in a property rather than a public field
    public Processor[] Processors;
    public Motherboard Motherboard;

    // Constructor
    public PC()
    {

        Motherboard = new Motherboard();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_Processor");
        Processors = new Processor[WMIData.Length-1];
        for (int i = 1; i < WMIData.Length; i++)
            {
            Processors[i-1] = new Processor(WMIData[i-1]); //assuming 0 based                
            }

        Motherboard.GetInfo();
    }
}

class Processor
{
    public string Architecture;
    public string Availability;
    public UInt16 Cores;

    public Processor(ManagementObject WMIData)
    {
        this.Architecture = (string)WMIData["Architecture"];
        this.Availability = (string)WMIData["Availability"];
        this.Cores = (UInt16)WMIData["NumberOfCores"];
    }
}

if you are worried about performance then you should hide your public fields behind properties and then make the calls lazily as you need them. Obviously this is a trade off between loading the data when you need it (which might involve delays at the time of access) or preloading all of it, (which might mean a delay at the start but will be quick when you need it). The desired result is down to whether you always needs all the data and how it is going to be used.

you could store the WMIData object in the Processor class and just read the values when you access the properties. It depends on where the slow bits are:

class Processor
{ 
    private ManagementObject WMIData;
    // obviously you might want to cache this value once it has been retrieved once
    public string Architecture{get{return (string)WMIData["Architecture"];}}
    public string Availability {get{return (string)WMIData["Availability"];}}
    public UInt16 Cores{get{return (UInt16)WMIData["NumberOfCores"]}}

    public Processor(ManagementObject WMIData)
    {
        this.WMIData = WMIData;
    }
}

EDIT

if you need more than 1 WMI query, then either pass the results of each WMI call to the object so it can get the data from them (sounds like it'll be slow) or give it just enough data so it can make those calls when it needs to:

class HardDrive
{
   private int index;
   private ManagmentObject physicalMediaInfo;
   private ManagementObject smartDataInfo;

   // choose one of these constructors.  this one lets you delay all the WMI calls till you need to do them 
   public HardDrive(int index)
   {
       this.index=index;
   }

   //this one means you have to make the calls in advance
   public HardDrive(ManagmentObject physicalMediaInfo,ManagementObject smartDataInfo)
   {
       this.physicalMediaInfo=physicalMediaInfo;
       this.smartDataInfo=smartDataInfo;
   }

   private ManagementObject PhysicalMediaInfo
   {
       get
       { 
          if(physicalMediaInfo==null)
          {
              ManagementObject[] WMIData = DataRetriever.GetWMIData("Win32_PhysicalMedia");
              physicalMediaInfo=WMIData[index];
          } 
          return physicalMediaInfo;         
       }
   }

   private ManagementObject SmartDataInfo
   {
       get
       { 
          if(smartDataInfo==null)
          {
              ManagementObject[] WMIData = DataRetriever.GetWMIData("ATAPI_SmartData");
              smartDataInfo=WMIData[index];
          } 
          return smartDataInfo;         
       }
   }

   //property for getting the details of the hard disk
   //uses the private property to ensure that the management object for the  is only loaded when its needed
   public int Sectors{get{return (int)PhysicalMediaInfo["Sectors"]};};

   //Same for the smart data.  
   public int SomeSmartData{get{return (int)SmartDataInfo["SomeSmartData"]};};

}

this approach allows you to only do each api call as a property from it is needed, and ensures that it is only done once regardless of how many properties from it are used. you could combine the constructors and allow the management objects passed in to be null, then you could provide the index which would be used to look up and ManagementObject instances which were not passed in with the constructor, but leave you free to pass in some ManagementObjects which could be preloaded if they were available or cheap...

EDIT 2

As for dealing with a refresh, assuming that refresh needs to ensure that next time data is accessed the latest info from the APi is requested you should be able to simply do this:

 public void Refresh()
 {
     this.physicalMediaInfo=null;
     this.smartDataInfo=null;
 }

This will then mean that next time Sectors is called the "Sectors" value will be re-queried from the WMIData as the existing WMIObject will be replaced.

6 Comments

Cores and Availability may not need to be arrays. I'm assuming cores is the number of cores a processor has, and availability is the availability of the processor
Issue with this idea is I cant necessarily complete Processor information with just one WMI query (as updated in the additional info). I might need several pieces of query data to successfully construct what I need. That's when performance starts dropping. Unless there's a way of merging ManagementObjects?
the principle is the same though, either get and pass all necessary data the object needs to the object when it is constructed, or ensure it has enough information to get it itself as and when it needs is. I'll add an update...
The edit is a much more realistic approach to the problem. It's a little bit bulky in my eyes, so once I implement this I'll share what I deem to be best for the structure but thanks anyway!
This code is getting more and more unmaintainable as I do this o.o I now have to worry about how it handles when a Refresh() command is invoked.
|
0

Thanks to everyone who's responded. I've come up with a reasonably elegant solution that suits my needs.

PC Class Example:

public class PC
{
    public List<Processor> Processor;

    // Constructor
    public PC()
    {
        this.Processor = new List<Processor>();
    }

    // Method to get all info sequentially
    public void GetAllInfo()
    {
        // These temporary stores fetch WMI data as ManagementObjects
        // Most cases will only need one WMI class.
        ManagementObject[] WMIDataTemp1;
        ManagementObject[] WMIDataTemp2;

        WMIDataTemp1 = DataRetriever.GetWMIData("Win32_Processor");
        foreach (ManagementObject Object in WMIDataTemp1)
        {
            this.Processor.Add(new Processor(Object));
        }
    }

    public void RefreshAll()
    {
        // Delete the lists and start again
        // Another option would be to foreach through the list elements and initialise each object again.
        this.Processor.Clear();
        GetAllInfo();
    }

    public void RefreshVolatileData()
    {
        // Extra function that will do some cool stuff later.
    }
}

Processor Class Example:

public class Processor
{
    // Define properties
    public string Architecture = "N/A";
    public string Availability = "N/A";
    public UInt32 CacheL2 = 0;
    public UInt32 CacheL3 = 0;

    // Overloaded constructor method
    // The one with no arguments does nothing to initialise the class
    // The one with the ManagementObject argument will call GetInfo to arrange the held data into the properties above
    public Processor() { }
    public Processor(ManagementObject wmiProcessor)
    {
        this.GetInfo(wmiProcessor);
    }

    // The main information handler for the classes.
    // This splits out the data in the ManagementObject into the class's own properties
    public void GetInfo(ManagementObject wmiProcessor)
    {
        // If anything fails, the try loop will just end without making a fuss
        // Because of the default values, N/A will be displayed everywhere if something fails here.
        try
        {
            this.Architecture = (string)wmiProcessor["Architecture"];
            this.Availability = (string)wmiProcessor["Availability"];
            this.CacheL2 = (UInt32)wmiProcessor["L2CacheSize"];
            this.CacheL3 = (UInt32)wmiProcessor["L3CacheSize"];
        }
        catch (Exception e)
        {

        }
    }
}

Usage example:

public PC Computer = new PC();
Computer.GetAllInfo();
textbox1.Text = Computer.Processor[0].Architecture

In the event a device needs to query more than one WMI class, each additional class can be listed as an extra parameter in the device constructor and GetInfo() method.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.