Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create and set a polymorphic property?

I want to create a class that can take different types of value in a property. I am trying to do this using polymorphism, but I am not still learning how to do this properly, hence my request for advice.

I have a base class and two classes that inherit from it:

public abstract class BaseClass
{
    public string Name { get; set; }
    public Unit Unit { get; set; }
}

public class DerivedClassFloat : BaseClass
{
    public float Value { get; set; }

    public override string ToString()
    {
        return Value.ToString();
    }
}

public class DerivedClassString : BaseClass
{
    public string Value { get; set; }

    public override string ToString()
    {
        return Value;
    }
}

All is good, I can create a List and add different specialized subclasses. My problem comes when I need change the values of the items in my list:

foreach (var item in ListOfBaseClasses)
{
   if(item is DerivedClassFloat)
     ((DerivedClassFloat) item).Value = float.NaN;
   if (item is DerivedClassString)
      ((DerivedClassString) item).Value = string.Empty;
}

According to what I have read, that looks like a code smell. Is there a better way to access the value property of my derived classes based on the type I am trying to assign?

What about when you want to create the right subclass based on the value?

BaseClass newClass = null;
if (phenotype is DerivedClassFloat)
    newClass = new DerivedClassFloat(){Value = 12.2};
if (phenotype is DerivedClassString)
    newClass = new DerivedClassString(){Value = "Hello"};                      

I read about overriding virtual methods, but that works if I want to process the value, not to add or change it … maybe I am missing something?


I should make this more concrete, my apologies, I am not used to post question in this great site.

I need a property that is made of a list of attributes. Each attribute has a name and a value, but the value can be of different types. For example:

public class Organism
{
    public string Name { get; set; }
    public List<Attribute> Attributes { get; set; }
}

public class Attribute
{
    public string AttributeName { get; set; }
    public object AttributeValue { get; set; }
}

For a given organism I can have several attributes holding different value types. I wanted to avoid using the object type so that I don’t have to cast to the right type. I though property polymorphism was the solution to handle this case elegantly, but then I found myself using If ..Then which didn’t seem too different from casting in the first place.

like image 989
Luciano Avatar asked Apr 21 '12 19:04

Luciano


3 Answers

If in your particular case you want to reset Value, you can define an abstract ResetValue method in the base class, which will be implemented by the derives classes.

As for your second case, you should check out Creational Design Patterns, and specifically the Factory and Prototype design patterns.

like image 127
Adi Lester Avatar answered Nov 08 '22 22:11

Adi Lester


You can use generics to define the type and the implementing subclass will set the Value type to the type constraint:

public abstract class BaseClass<T>
{
    public string Name { get; set; }
    public Unit Unit { get; set; }
    public T Value { get; set; }

    public override string ToString()
    {
        return Value.ToString();
    }
}

public class DerivedFloat : BaseClass<float> {}

public class DerivedString : BaseClass<string> {}
like image 40
Metro Smurf Avatar answered Nov 08 '22 21:11

Metro Smurf


You can use Generics for this particular case:

public abstract class BaseClass<T>
{
    public string Name { get; set; }
    public Unit Unit { get; set; }
    public T Value { get; set; }
}

public class DerivedClassFloat : BaseClass<float>
{
    public override string ToString()
    {
        return Value.ToString();
    }
}

public class DerivedClassString : BaseClass<string>
{
    public override string ToString()
    {
        return Value;
    }
}
like image 38
Dennis Traub Avatar answered Nov 08 '22 20:11

Dennis Traub