1

I want to replace the struct in the following code with a parent class with no data members and four subclasses, each subclass adds a different field. e.g. The SMValueFlt subclass adds a field named fltValue, and so on.

I am very new to C# and my Java is very rusty, so this is proving harder than I thought. And beyond actually setting up the class and subclasses i'm not sure how to proceed. Any help would be appreciated.

public class Interpreter {


    enum TypeCode { None, IntType, FloatType, StringType };


    struct SMValue {
        public TypeCode t;
        public int intValue;
        public float fltValue;
        public string strValue;
        public SMValue( int i )    {
            t = TypeCode.IntType;  intValue = i;  fltValue = 0.0F;  strValue = null; }
        public SMValue( float f )  {
            t = TypeCode.FloatType;  fltValue = f;  intValue = 0;  strValue = null; }
        public SMValue( string s ) {
            t = TypeCode.StringType;  strValue = s;  intValue = 0;  fltValue = 0.0F; }
        public override string ToString() {
            if (t == TypeCode.IntType) return String.Format("{0}", intValue);
            if (t == TypeCode.FloatType) return String.Format("{0}", fltValue);
            if (t == TypeCode.StringType)
                return strValue==null? "--null--" : strValue;
            return "???";
        }
    }
}
2
  • So you want to have a class that can be initialized with a value that is of different types based on its constructor (like string, int, ...), that sounds like a job for Generics. Commented Feb 13, 2014 at 16:23
  • 1
    I see how generics would probably work best, but I want to specifically use four subclasses for int, float, string, None Commented Feb 13, 2014 at 16:51

2 Answers 2

2

I kept your TypeCode around in the first example, but it's not really necessary. You can inspect the type of a variable at runtime. For example,

var x = new SMFltValue() // (x.GetType() == typeof(SMFltValue)) = true, x is SMFltValue = true

Without using generics:

public enum TypeCode { IntType, FloatType, StringType };

public abstract class SMValue {
    public TypeCode t;

    public SMValue(TypeCode typeCode) {
        t = typeCode;
    }

    public abstract string ToString();
}

public class SMFltValue : SMValue {
    public float fltValue;

    public SMFltValue(float f) : base(TypeCode.FloatType)
    {
        fltValue = f;
    }

    public override string ToString() 
    {
        return String.Format("{0}", fltValue);
        return String.Format("{0}", intValue);
        return strValue==null ? "--null--" : strValue;
    }
}

public class SMIntValue : SMValue {
    public int intValue;

    public SMIntValue(int i) : base(TypeCode.IntType)   
    {
        intValue = i;
    }

    public override string ToString() 
    {
        return String.Format("{0}", intValue);
    }
}

public class SMStrValue : SMValue {
    public string strValue;

    public SMStrValue(string s) : base(TypeCode.StringType)
    {
        strValue = s;
    }

    public override string ToString() 
    {
        return strValue==null ? "--null--" : strValue;
    }
}

But generics would make it much nicer.

public class SMValue<T> {
    public T value;

    public SMValue(T value) {
        this.value = value;
    }

    public string ToString() {
        if (value == null) 
        {
            return "--null--";                  
        }
        else 
        {
            return string.Format("{0}", value);
        }
    }
}

Then you could use it as.

int i = 3;
float f = 5.0f;
string s = null;

new SMValue<int>(i).ToString() ==> 3
new SMValue<float>(f).ToString() ==> 5.0
new SMValue<string>(s).ToString() ==> "--null--"

The <int>, <float>, <string> aren't actually necessary because the compiler can infer the type from the variable being passed to the constructor.

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

2 Comments

Ok, that kind of makes sense. One thing though, coming from Java (ages ago) don't I need get and set properties for these classes? And if I left out TypeCode, the parent class would be empty except for ToString? And I would ommit base(TypeCode.IntType) etc?
Well, I didn't want to nitpick, but yeah. You should use public T Value { get; set; } instead of public T value; (note the V in value). This is C# style for properties which is analagous to String getValue() and void setValue(String value) in Java. You can remove all references to TypeCode and then you wouldn't need the : base(...) at all (: base() is implied when it's omitted here). If you go the generic route, you can simply change public T value; to public T Value { get; set; } and update the references. This would support SMValue<bool> and other things you don't anticipate here too.
0

The semantics of a struct with exposed fields are fundamentally different from those of a class. Fundamentally, each structure-type variable holds a bunch of fields stuck together with duct tape, while a class-type variable holds a not-necessarily-unique reference to a class object. If a structure type has two int fields, and one has two variables of that type, one has four integers which may be written independently. By contrast, if a class type has two int fields and one has two variables of that type, it's possible that the variables may at any given time reference different instances (in which case they would encapsulate a total of four independently-writable integers), or they may identify the same instance (in which case both variables would identify the same pair of integers, and so writing the first number in one pair would also write the first number in the other).

Some people think all types should behave like class objects, and regard as "evil" any types that don't. In reality, there are situations where it's useful to stick a bunch of variables together with duct tape (so they may be passed around as a unit when convenient), but guarantee that every bunch of variables is distinct. Class types can be used to mimic this behavior, awkwardly, but structures naturally work that way.

Without knowing exactly how you intend to use your type, it's hard to say whether a class will be able to fulfill your needs without having to rework all your client code. It's important to note, however, that any class used to replace a struct must almost always be immutable. If you can't easily convert your struct to a mutable class, you'll probably have to keep it a struct.

1 Comment

Yes, I am having trouble reworking the rest of my code, but I'll hack away at it a little longer before asking for help!

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.