tags:

views:

187

answers:

3

I'm writing a plug-in for an application with a .NET API. The objects of the program can have custom attributes applied through two methods of the root object type which assign key/value pairs to the objects.

BaseAppObject.GetUserString(string key, string value);
BaseAppObject.SetUserString(string key, ref string value);

I'm creating a set of my own custom classes that act as wrapper classes around instances of BaseAppObject. All my classes are derived from a class Node which has a field to store a reference to a BaseAppObject. Other properties of Node and types that derive from Node use the GetUserString and SetUserString methods of the associated BaseAppObject instance to read or write property values directly to or from the associated BaseAppObjects. This way when the application is closed all the information needed to regenerate these wrapper classes later is stored in the actual document.

Here's a simplified version of what I have for my base class constructor.

public abstract class Node
{
    BaseAppObject _baseObject;

    public Node(BaseAppObject baseObject, string name)
    {
        this._baseObject = baseObject;
        this.Name = name;
    } 

    public string Name
    {
        get { 
                string name = "";
                _baseObject.GetUserString("CPName", ref name);
                return name;             
            }

        set {
                _baseObject.SetUserString("CPName", value);
            }
    }
}

Other classes derived from Node may add additional properties like this.

public CustomClass:Node
{
    public CustomClass(BaseAppObject baseObj,string name, string color):base(baseObj,name)

    public string Color
    {
        get { 
                string name = "";
                this.BaseObject.GetUserString("Color", ref name);
                return name;             
            }

        set {
                this.BaseObject.SetUserString("Color", value);
            }
    }
}

I'm trying to figure out the best way to setup the constructors and other methods of my classes to initiate and regenerate instances of my classes. I need to be able to create new instances of my classes based of clean instances of BaseAppObject that have no user strings defined, and also regenerate previously existing instance of my class based on the user strings stored in a existing BaseAppObject.

+1  A: 

It looks like you have already figured out how to regenerate previously existing classes. To generate a clean object with no values, all you have to do is provide an additional overload that takes no parameters and creates _baseObject. Once the object is created in this manner, you can use it's properties to set the member values.

public abstract class Node
{
    BaseAppObject _baseObject;
    //Empty constructor
    public Node() 
    {
        BaseAppObject = new BaseAppObject();
    }
    public Node(BaseAppObject baseObject, string name)
    {
        this.BaseObject = baseObject;
        this.Name = name;
    } 

    public string Name
    {
        get { 
                string name = "";
                _baseObject.GetUserString("CPName", ref name);
                return name;             
            }

        set {
                _baseObject.SetUserString("CPName", value);
            }
    }
}

public CustomClass:Node
{
    // Empty constructor
    public CustomClass() : Base() {}
    public CustomClass(BaseAppObject baseObj,string name, string color):base(baseObj,name)

    public string Color
    {
        get { 
                string name = "";
                this.BaseObject.GetUserString("Color", ref name);
                return name;             
            }

        set {
                this.BaseObject.SetUserString("Color", value);
            }
    }
}

Sample code for creating an empty object and populating it:

CustomClass node = new CustomClass();
node.Name = "foo";
node.Color = "bar";
Robert Harvey
Rather then creating an empty class I think it would be better to have a constructor that takes only a BaseAppObject argument.
Eric Anastas
The code above omits a public property in the base class that is required for the wrapping class to have access to the _baseObject.
Robert Harvey
Huh? Your Node constructor makes no sense. You can't take the "BaseAppObject baseObject" argument and set the this.BaseObject property equal to it. This.BaseObject is a string property not a BaseAppObject property.
Eric Anastas
That was in your original code, Eric.
Robert Harvey
Whoops ...sorry ; - )
Eric Anastas
That still doesn't work though. I can't just instantiate a BaseAppObject. The application is a 3D modeling program and the BaseAppObject is the base geometry object. The only way one can gain access to an instance of a BaseAppObject is finding a reference to an exsisting on already in the document.
Eric Anastas
Ultimately I think having a constructor for initializing these wrapper classes and setting up the User Strings, and another for reinitializing them later from the user strings will work. My only gripe is it doesn't seem that there is a way to force this kind of implementation. If I/someone else created a derived class from my base class I would have to remember/tell them that the correct way is to create a constructor for initialization and another for reinitialization. So I was hoping there was some way I could make this implemention intrinsic to the class itself.
Eric Anastas
The manner in which the constructors are used should be implicit in the signature of the constructors, and the presence of setter properties for the missing parameters in the default constructor. In other words, developers recognize this pattern. I also assume you will be including eloquent XML comments. :)
Robert Harvey
If developers are not supposed to derive the class, seal it.
Robert Harvey
+1  A: 

I have written a base Wrapper Object that I have all my wrappers inherit from.

public abstract class BaseWrapper<TBaseType> 
 where TBaseType : class
{
 protected readonly TBaseType BaseItem;

 protected BaseWrapper(TBaseType value)
 {
  BaseItem = value;
 }

 public bool IsNotNull()
 {
  return BaseItem != null;
 }
}
zonkflut
Heh that's not a bad idea. It doesn't really apply to my case since I know I'll always be wrapping the same type of object.
Eric Anastas
A: 

I don't see why you don't want to make Node a child class of BaseClassObj. If you're writing a wrapper class, you can use the type-casting features to make your "Node" class and all it's children directly compatible with the BaseClassObj.

If you're doing that, you can just say

public abstract class Node : BaseAppObject
{

    public Node(string name) : base()
    {            
        this.Name = name;
    } 

    public string Name
    {
        get { 
                string name = "";
                this.GetUserString("CPName", ref name);
                return name;             
            }

        set {
                this.SetUserString("CPName", value);
            }
    }
}

Now you can use a Node object just like a BaseClassObj, and still use the additional functionality. I think this is a better way to implement a wrapper class. The typecasting in C# is fantastic and you should take advantage of it.

antileet
Alright but how do I wrap an existing instance of BaseAppObject with this version of Node? I can't create instances of BassAppObject but rather get references to them as the user select them.
Eric Anastas