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\"";
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\"";
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.
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.
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";
}
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";
}
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).
What about using delegates?
Action statement1 = () => button1.Visible = true;
Action statement2 = () => button1.Text = "Number";