-4

I want to execute a C# statement which is stored in a string variable. For instance:

string statement1 = "button1.Visible = true";
string statement2 = "button1.Text = \"Number\"";
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
Aamir Ali
  • 145
  • 2
  • 16

3 Answers3

2

Looking at your comments and that you have 80 controls requiring very similar action, dynamic compilation may be an overkill for this purpose. You can use use Controls collection of the parent container along with the Tag property of your buttons to achieve it. A single event handler would suffice.

You can use LINQ members like OfType and Cast to make your code even smaller.

Edit

After looking at your latest comment, what you should do is to programmatically create your buttons and add them to your Form or whatever container you have. You can then keep a Dictionary<string, Button> that will let you either iterate over the collection, or access an individual button through its name. Something like:

//Declare this globally
Dictionary<string, Button> Dic = new Dictionary<string, Button>(81);

//put this in the constructor
for(int i=0; i<81; i++)
{
    Button b = new Button();
    b.Text = i; //or Random or whatever
    b.Name = "btn" + i.ToString();
    this.Controls.Add(b);
    Dic.Add(b.Name, b);
}

Later you can do both iteration like this:

foreach(var item in Dic)
{
    item.Value.Visible = true; //or false
}

and key-based access like this:

Dic["btn45"].Visible = true; //or false

Since you're creating Sudoku, i probably think you should use TableLayoutPanel with appropriate number of rows and columns at design-time and then add your buttons to the panel and set their Row/Column property in the loop. This will help better respond to resizing events etc.

dotNET
  • 33,414
  • 24
  • 162
  • 251
1

From your comments it appears you simply want to iterate over 80 similar buttons and configure them the same way. This can be accomplished by far simpler means than executing dynamic code.

Here's a couple of ways.

Use the tag property

First set the Tag property to a specific number in the designer for each of the 80 buttons.

Then execute this code:

foreach (var button in Controls.OfType<Button>().Where(button => button.Tag == "TAG"))
{
    button.Visible = true;
    button.Text = "Number";
}

Use the name to identify them:

Ensure all the 80 buttons have a name of "buttonX" where X is a number.

Then execute this code:

foreach (var button in Controls.OfType<Button>().Where(button => button.Name.StartsWith("button"))
{
    button.Visible = true;
    button.Text = "Number";
}

Actually "execute code"

If, as you say in your comments, you only need to solve this problem: "Execute" this type of code:

object.member=value

Where object refers to something stored in a field or property. member refers to a public member (field or property) of that object, and value is something that will always be easily convertible to the member type, then the following code will work.

Note that it is short on error checking, so please make sure you vet the code before using it.

ALSO I am not convinced in the very slightest that this is the appropriate solution, but since you've decided to ask about the solution you had in mind instead of the actual problem, it's hard to come up with a better solution.

You can run this code in LINQPad:

void Main()
{
    button1.Dump();
    SetProperties(this,
        "button1.Visible=true",
        "button1.Text=\"Number\""
    );
    button1.Dump();
}

public static void SetProperties(object instance, params string[] propertySpecifications)
{
    SetProperties(instance, (IEnumerable<string>)propertySpecifications);
}

public static void SetProperties(object instance, IEnumerable<string> propertySpecifications)
{
    var re = new Regex(@"^(?<object>[a-z_][a-z0-9_]*)\.(?<member>[a-z_][a-z0-9_]*)=(?<value>.*)$", RegexOptions.IgnoreCase);
    foreach (var propertySpecification in propertySpecifications)
    {
        var ma = re.Match(propertySpecification);
        if (!ma.Success)
            throw new InvalidOperationException("Invalid property specification: " + propertySpecification);

        string objectName = ma.Groups["object"].Value;
        string memberName = ma.Groups["member"].Value;
        string valueString = ma.Groups["value"].Value;
        object value;
        if (valueString.StartsWith("\"") && valueString.EndsWith("\""))
            value = valueString.Substring(1, valueString.Length - 2);
        else
            value = valueString;

        var obj = GetObject(instance, objectName);
        if (obj == null)
            throw new InvalidOperationException("No object with the name " + objectName);

        var fi = obj.GetType().GetField(memberName);
        if (fi != null)
            fi.SetValue(obj, Convert.ChangeType(value, fi.FieldType));
        else
        {
            var pi = obj.GetType().GetProperty(memberName);
            if (pi != null && pi.GetIndexParameters().Length == 0)
                pi.SetValue(obj, Convert.ChangeType(value, pi.PropertyType));
            else
                throw new InvalidOperationException("No member with the name " + memberName + " on the " + objectName + " object");
        }
    }
}

private static object GetObject(object instance, string memberName)
{
    var type = instance.GetType();
    var fi = type.GetField(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
    if (fi != null)
        return fi.GetValue(instance);

    var pi = type.GetProperty(memberName, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Default);
    if (pi != null && pi.GetIndexParameters().Length == 0)
        return pi.GetValue(instance, null);

    return null;
}

private Button button1 = new Button();

public class Button
{
    public bool Visible;
    public string Text { get; set; }
}

This will output (the two button1.Dump(); statements) the button configuration before and after, and you'll notice that the property and the field have been set.

You can execute this as follows:

`SetProperties(this, "...", "...");

where this must refer to your form object (the one that owns the buttons).

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • I have a hard time imagining a game screen with 80 (EIGHTY that is) buttons each saying "Number", and those too appearing at once... :) – dotNET Sep 30 '13 at 06:18
  • Yes, me too, but I have a hard time figuring out what else it could mean. – Lasse V. Karlsen Sep 30 '13 at 06:21
  • "Number" was just an example actually its a sudoku game containing 81 buttons, btw thanks for help @dotNET and @ Lasse :) – Aamir Ali Sep 30 '13 at 06:27
  • Then again, I would not use the approach you've asked for, I would either name the 81 buttons appropriately, or store them in arrays for easy reference or whatnot. – Lasse V. Karlsen Sep 30 '13 at 06:28
  • Ah i see. Yes only sudoku and minesweeper kind of games can afford that number of buttons. See my edit. – dotNET Sep 30 '13 at 06:29
1

What about using delegates?

Action statement1 = () => button1.Visible = true;
Action statement2 = () => button1.Text = "Number";
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193