Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#: Properties with different return types on derived classes

I tried to search for an answer for this problem but could not find much, most probably because I do not know how to look for it properly, so here it goes. All help is very much appreciated.

With the base class that looks like

abstract public class Property
{
    private String name;

    public Property(String propertyName)
    { 
        name = propertyName; 
    }

    public String Name
    {
        get { return name; }
    }

    abstract public override String ToString();
}

And derived classes that look like

public class StringProperty : Property
{
    private String value; // different properties for different types

    public StringProperty(String propertyName, String value) : base(propertyName)
    {
        this.value = value;
    }

    public String Value // different signature for different properties
    {
        get { return value; }
    }

    public override String ToString()
    {
        return base.Name + ": " + value;
    }
}

During runtime, the function receives a collection of "Property" objects. What do I need to do to be able to obtain the "Value" of each? Do I need to have a big if statement to query the type of each "Property" object? If not, is there a more elegant solution?

I tried to define an abstract "Value" property to be overridden but since the return types are different, it did not work. I also tried playing with shadowing the "Value" property, but I could not make it work. The idea of using an COM-like Variant does not sound very appropriate, either.

Thanks a lot in advance.

EDIT:

I should have added details as to what I am trying to do. The properties are displayed in a Winforms app. Different "TextBox"es represent different properties and are filtered for proper input (depending on the type). The updated values are read back and stored. The container object will be serialized into JSON and deserialized on an Android and iPhone client and eventually these values will be passed into a layer running native C++ code doing OpenGL stuff. I don't know in advance the kind of all needed properties so as the middleman, I wanted to make my code as robust as possible while being able to feed the OpenGL engine.

like image 987
alokoko Avatar asked Nov 12 '11 22:11

alokoko


4 Answers

You can use a generic class:

public class AnyProperty<T> : Property
{
    private T value;
    // ... etc

I'd really recommend making the base class an Interface by now:

public interface IProperty
{
    public String Name { get; }
}

public class Property<T> : IProperty
{
    public Property(String name, T value)
    {
        Name = name;
        Value = value;
    }

    public String Name { get; private set; }
    public T Value { get; private set; }

    public override String ToString()
    {
        return string.Format("{0}: {1}", Name, Value)
    }
}

Here is sample usage:

var intProp = new Property<int>       ("age", 32);    
var strProp = new Property<string>    ("name", "Earl");    
var enumProp = new Property<ColorEnum> ("eye color", ColorEnum.Magenta);    

To make the construction even simpler, you could have a factory method:

public static Property<T> MakeProperty(string name, T value)
{
    return new Property<T>(name,value);
}

var intProp = MakeProperty("age", 32);    
var strProp = MakeProperty("name", "Earl");    
var enumProp = MakeProperty("eye color", ColorEnum.Magenta);    

Not necessarily recommended, and a bit OT:

You could make it even funkier with an extension method:

public static Property<T> AsProp<T>(this T value, string name)
{
    return new Property<T>(name,value);
}

var intProp = 32.AsProp("age");
var strProp = "Earl".AsProp("name");
var enumProp = ColorEnum.Magenta.AsProp("eye color");

like image 81
sehe Avatar answered Oct 18 '22 20:10

sehe


You would have to simply use the object type. What are you trying to accomplish? The problem here isn't the structure of your classes, it's the function that receives the collection of Property objects. It's impossible to even cast something to an unknown type, since you don't know what type of variable it needs to be stored in.

So basically, your Property.Value property needs to be of type object. In your method that uses the Property objects, you need to do something with them, and what you're doing will decide how it should be structured. Are you printing values out? Have a *Value class inheriting from an abstract PropertyValue class and override ToString() to return an appropriate string represention.

like image 22
Ry- Avatar answered Oct 18 '22 18:10

Ry-


I made a few changes to your sample code and got this result...

   abstract public class Property
    {
        private readonly String _name;
        public Property(String propertyName)
        {
            _name = propertyName;
        }
        public String Name
        {
            get { return _name; }
        }
        abstract public override String ToString();
    }
    public class StringProperty : Property
    {
        private readonly dynamic _value; // different properties for different types 
        public StringProperty(String propertyName, dynamic value)
            : base(propertyName)
        {
            this._value = value;
        }
        public dynamic Value // different signature for different properties 
        {
            get { return _value; }
        }
        public override String ToString()
        {
            return base.Name + ": " + _value;
        }
    }
    static void Main(string[] args)
    {
        StringProperty sp = new StringProperty("A double", 3.444);
        StringProperty sp2 = new StringProperty("My int", 4343);
        StringProperty sp3 = new StringProperty("My directory", new  DirectoryInfo("Some directory"));
        StringProperty sp4 = new StringProperty("My null", null);
        Console.WriteLine(sp);
        Console.WriteLine(sp2);
        Console.WriteLine(sp3);
        Console.WriteLine(sp4);
    }
}

Values are properly printed to the console in the expected way.

like image 3
Gayot Fow Avatar answered Oct 18 '22 18:10

Gayot Fow


It would require a bit of a rethink, but have you considered using the dynamic type (introduced in .net4)

Doesn't really solve your problem, but sidespteps it. Your properties can bascically just be a

Dictionary<String, dynamic>

, the gotcha is they don't get evaluated until runtime, so you get no compiler support for typing.

so given you want int SomeValue = MyProperties[SomePropertyName] + 10;

So if MyProperties[SomePropertyName] = 10; // all is good

if its 76.52 or Fred, the addition will throw an exception at the point it executes.

Code is much simpler and cleaner, no extra casting and the amount of scaffolding required is minimal, BUT, you'll need to unit test code that uses the dictionary extensively and religiously.

like image 2
Tony Hopkinson Avatar answered Oct 18 '22 18:10

Tony Hopkinson