3

I'm trying to create a simple way for external programs to get/set variables within my class. What I have is a class with several variable such as this:

class myClass
{
    public int one
    {
        get{ /* get code here */ }
        set{ /* set code here */ }
    }
}

Rather than just variable 'one' I have close to 100 variables. All are set up the same way with get and set code. What I would like to have is a simple way to get and set the variables. Example: Instead of having to do this:

myClass c = new myClass();
c.one = 5;

I would like to find a way to do something similar to this:

myClass c = new myClass();
c.setVariable("variableName", value);

It would be ideal to have the "variableName" text come from an enum list so that they could be referenced like:

c.setVariable(enumName.varName, value);

I am unsure how to go about this or if it is even possible. As I said i have close to 100 variables which need their own get/set code, but I'd prefer to have only one public get function and one public set function for various reasons.

As I don't think reflection is very efficient, I'd like to avoid it if at all possible.

I've seen other code in C# where something like this is used:

this["variableName"] = value;

However I can't seem to find a way to make this work either...

8
  • 3
    You can't avoid reflection in this case. Commented Feb 22, 2013 at 1:18
  • as Fuex said Reflection is a must in this case! Commented Feb 22, 2013 at 1:19
  • 3
    Just use a Dictionary instead. Commented Feb 22, 2013 at 1:19
  • 2
    In what way is c.SetVariable(myClassEnum.one, 5) better than c.one = 5 ? - I don't get it. Commented Feb 22, 2013 at 1:22
  • Make your class an implementation of IDictionary<T> and you can do what you were looking for. It won't give you properties but maybe that's not what you need Commented Feb 22, 2013 at 1:26

3 Answers 3

7
this["variableName"] = value;

The syntax you describe is only valid for dictionaries. And if you don't want to use reflection and only want a single getter and setter, you should maybe use a dictionary. Edit: Do you know about lambda expressions? They are inline anonymous methods, and by putting those in another dictionary you can do validation! Something like this:

public class MyClass
{
    private readonly Dictionary<String, Object> dictionary;
    private readonly Dictionary<String, Func<Object, bool>> validators;

    public MyClass()
    {
        this.dictionary = new Dictionary<String, Object>();
        this.validators = new Dictionary<String, Func<Object, bool>>();

        // Put the default values in the dictionary.
        this.dictionary["One"] = 10;
        this.dictionary["Another"] = "ABC";

        // The validation functions:
        this.validators["One"] = obj => ((int)obj) >= 0;
        this.validators["Another"] = obj => obj != null;
    }

    public Object this[string name]
    {
        get { return this.dictionary[name]; }
        set
        {
            // This assumes the dictionary contains a default for _all_ names.
            if (!this.dictionary.Contains(name))
                throw new ArgumentException("Name is invalid.");

            // Get a validator function. If there is one, it must return true.
            Func<Object, bool> validator;
            if (validators.TryGetValue(name, out validator) &&
                !validator(value))
                throw new ArgumentException("Value is invalid.");

            // Now set the value.
            this.dictionary[name] = value;
        }
    }
}

Some things to note about this code:

  • The dictionary is a generic object: Dictionary Of String and Object, where String is the type of the key and Object is the type of the value. If you have only one kind of value (for example integers) then you should change Object everywhere to, for example, int.
  • Given an instance MyClass myClass, you can do myClass["One"] = 20 to set the value of One.
  • If you (inadvertently) try to get the value with a name that does not exist (var value = myClass["DoesNotExist"]), you'll get an exception. You can change this to return a default value or do something else entirely.
  • To use an enum, create the enum and specify its type instead of String in the code.
Sign up to request clarification or add additional context in comments.

5 Comments

I like this solution however I see one issue. I need different code to set / get each variable. Some variables have different valid ranges and an error needs thrown if the value is about to be set to something invalid.
Then you need to explicitly create properties for each variable! You can group them if they share some common factors, but you're going to end up writing so much "simplifying code" that you may as well have just added the properties in the first place.
@JasonLarke I know I need to create properties for each, however C# will not let you create indexed properties such as above with the same key. So instead of multiple with [string name] I would have to change the key for each property.
@Alan: I used lambda functions in a dictionary to allow you to validate the values. When you don't need validation for a particular name, you can either return true from the lambda function or just omit it.
@Virtlink Thanks for the edit! I had not thought about the usefulness of lambda functions for this. Looks good.
1

Another solution could be use reflection in this way:

public object this[string name] {
    get { return this.GetType().GetProperty(name).GetValue(this, null); }
    set { this.GetType().GetProperty(name).SetValue(this, value, null); }
}

or:

public void SetVariable(string name, object value){
    this.GetType().GetProperty(name).SetValue(this, value, null); 
}

4 Comments

I like this solution, what is the reason not to use it ?
This is just reflection though. You mentioned you didn't want it.
Although I specified I didn't want to use Reflection, it's not totally out of the question and I do like the simplicity of this solution.
@Turbot Hi I was wondering if the value for set is a function (lets say you want myObject[MyClass.Variables.one] = () => {doSomethingElse()}, then how do I activate that function, since value is an object type?
0

I won't comment on the design, but it's rather simple to implement. You don't need reflection at all, you just need a lot of typing. In general, what you want is basically this:

public class MyClass
{
    public enum Variables {
        one,
        two
    }

    private int one {
        get { /* get; */ }
        set { /* set; */ }
    }

    private int two {
        get { /* get; */ }
        set { /* set; */ }
    }

    public object getVariable(Variables v) {
        switch(v) {
            case Variables.one: return one;
            case Variables.two: return two;

            default: throw new InvalidArgumentException("v");
        }
    }

    public void setVariable(Variables v, object value) {
        switch(v) {
            case Variables.one: one = (int)value; return;
            case Variables.two: two = (int)value; return;

            default: throw new InvalidArgumentException("v");
        }
    }
}

Ofcause, you got no type-checking, so a lot of things can blow up in your face. Another way to do this with the use of reflection without causing too much performance-strain would be to statically compile a dictionary of enum->getter and enum->setter methods (not just the MethodInfo, but a delegate). This would induce a start-up performance-hit. But it would not (afaik) damage performance (notably) when getting and setting values.

[Edit]
The above can also be implemented as this:

public object this[Variables v]
{
    get { return getVariable(v); /* or move implementation here */ }
    set { serVariable(v, value); /* or move implementation here */ }
}

This would enable you to write (for instance) myObject[MyClass.Variables.one] = 5.

[Edit 2]
The following has been tested working. I have no idea of performance when put up against normal reflection (or huge switch for that matter).

3 Comments

I'm still slightly concerned about a performance hit using a giant switch. This was my initial though of how to solve the problem however my program may be setting variable several (possibly close to 50) times per second. Opinions on this?
Switchs are supposed to be really fast (as far as I've been told). They compile into jump-tables, and are a lot faster than if..elseif..elseif.........else (with the exception of the corner case where you know which one is most likely to appear).
@Alex Hi I was wondering if the value for set is a function (lets say you want myObject[MyClass.Variables.one] = () => {doSomethingElse()}, then how do I activate that function, since value is an object type?

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.