So I want to build this little code sandbox in Unity, which would allow me to teach students the basics of algorithmics and coding.
The idea would be for them to enter (very basic) code in a text box or something of the kind, and to observe the effects of their code onto objects present in a Unity scene. I'm pretty sure this has been done a million times, but I'd love to try my hand at this. The rub is, I have no idea where to start...
I guess the idea is that the string would be compiled into code & executed at runtime, at the press of a button.
I've read about numerous other questions on SO, and have come up with very diverse solutions such as using a C# parser, reflection, expression trees, CodeDom, etc.
From what I understood of all these (i.e., not much), CodeDom seemed more appropriate, but then I read that it only ran inside of Visual Studio and generated errors in public builds. So does that mean that this is going to be a problem within Unity3D (as it is based on Mono?)
Thank you for your help,
In the following case, you look for an existing method of the given name on the same script (you can easily convert it to another script or any script in the assembly (not recommended though)):
string actionStr = inputField.text;
Type t = this.GetType();
MethodInfo mi = t.GetMethod(actionStr);
if(mi == null)
{
ErrorMethod(actionStr + " method could not be found");
}else
{
mi.Invoke(this);
}
Another way would be to store all the methods in a dictionary (faster):
Dictionary<string, Action>dict = null;
void Start()
{
this.dict = new Dictionary<string, Action>();
this.dict.Add("dosomething", DoSomething);
}
void DoSomething(){}
public void OnActionCall(string inputFieldStr)
{
string str = inputFieldStr.ToLower();
if(this.dict.Contains(str) == false)
{
ErrorMethod(actionStr + " method could not be found");
return;
}
this.dict[str]();
}
Related
I am trying to create a C# program that lets user's provide an implementation for for a function by inputting text into a text box. I provide the function header (input types, output type), they just need to provide actual implementation. I then store that function to call later. They might need to import something from the .NET framework, but nothing outside of it.
I don't care about security, this is just for a tool for internal use.
Is there an easy way to do this in .NET?
The usage would look something like (need to implement the CompileUserFunction function, which takes in an int and returns an object):
Func<int, object> CreateUserFunction(string input) {
Func<int, object> userFunc = CompileUserFunction(input);
return (i) => userFunc(i);
}
public void DoSomething() {
List<Func<int, object>> userFuncs = new List<Func<int, object>>();
string userInput = #"DateTime t = DateTime.Now;
t.AddDays(i);
return t;";
userFuncs.Add(CreateUserFunction(userInput));
userFuncs.Add(CreateUserFunction("return i;"));
userFuncs.Add(CreateUserFunction("i = i * 5; return i;"));
var result = userFuncs[0](5);
}
You can use code generation libs for that task.
I advice you to use Roslyn scripting API. I have done a similar task - parsing a string into delegate with it. The following example is taken from this link: https://blogs.msdn.microsoft.com/csharpfaq/2011/12/02/introduction-to-the-roslyn-scripting-api/
You will find there more examples
using Roslyn.Scripting.CSharp;
namespace RoslynScriptingDemo
{
class Program
{
static void Main(string[] args)
{
var engine = new ScriptEngine();
engine.Execute(#"System.Console.WriteLine(""Hello Roslyn"");");
}
}
}
There are other code generation tools and libs:
CodeDom - an old .Net code generation Framework. Probably can be used here but is more tricky.
https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/using-the-codedom
There were some libraries which were used to convert strings to Linq Expression trees, but it all seems to be outdated now.
There is also a possibility to create a Dynamic Method via Reflection.Emit but it is very low level - you need to define method implementation in IL instructions.
This may be a noob question, but I need some help. I have written two simple methods in C#: ReadCsv_IT and GetTranslation. The ReadCsv_IT method reads from a csv file. The GetTransaltion method calls the ReadCsv_IT method and returns the translated input (string key).
My problem is that in the future I will have to request a lot of times GetTranslation, but I obviously don't want to read the .csv files every time. So I was thinking about ways to use cache Memory to optimize my program, so that I don't have to read the .csv file on every request. But I am not sure how to do it and what I could do to optimize my program. Can anyone please help ?
public string ReadCsv_IT(string key)
{
string newKey = "";
using (var streamReader = new StreamReader(#"MyResource.csv"))
{
CsvReader csv = new CsvReader(streamReader);
csv.Configuration.Delimiter = ";";
List<DataRecord> rec = csv.GetRecords<DataRecord>().ToList();
DataRecord record = rec.FirstOrDefault(a => a.ORIGTITLE == key);
if (record != null)
{
//DOES THE LOCALIZATION with the help of the .csv file.
}
}
return newKey;
}
Here is the GetTranslation Method:
public string GetTranslation(string key, string culture = null)
{
string result = "";
if (culture == null)
{
culture = Thread.CurrentThread.CurrentCulture.Name;
}
if (culture == "it-IT")
{
result = ReadCsv_IT(key);
}
return result;
}
Here is also the class DataRecord.
class DataRecord
{
public string ORIGTITLE { get; set; }
public string REPLACETITLE { get; set; }
public string ORIGTOOLTIP { get; set; }
}
}
Two options IMO:
Turn your stream into an object?
In other words:
Make a class stream so you can refer to that object of the class stream.
Second:
Initialize your stream in the scope that calls for GetTranslation, and pass it on as an attribute to GetTranslation and ReadCSV_IT.
Brecht C and Thom Hubers have already given you good advice. I would like to add one more point, though: using csv files for localization in .NET is not really a good idea. Microsoft recommends using a resource-based approach (this article is a good starting point). It seems to me that you are trying to write code for something that is already built into .NET.
From a translation point of view csv files are not the best possible format either. First of all, they are not really standardized: many tools have slightly different ways to handle commas, quotes, and line breaks that are part of the translated text. Besides, translators will be tempted to open them in Excel, and -unless handled with caution- Excel will write out translations in whatever encoding it deems best.
If the project you are working on is for learning please feel free to go ahead with it, but if you are developing software that will be used by customers, updated, translated into several target languages, and redeployed, I would recommend to reconsider your internationalization approach.
#Brecht C is right, use that answer to start. When a variable has to be cached to be used by multiple threads or instances: take a look at InMemoryCache or Redis when perfomance and distribution over several clients gets an issue.
So I have a cmdlet written in c#: Get-LivingCharacter. I want users to use this like Get-LivingCharacter -Name "Bran", but I would like to allow for the list of available characters to change. Maybe today, "Bran" is a valid name to pass in for Get-LivingCharacter, but maybe in the future it will not be. Things happen.
For convenience I want to allow tab-completion of this field. However, I can't seem to get that to work for non-const data sets. Dynamic fields don't even auto-complete the field name, nevermind the value, and I don't know a way to implement this for a non-dynamic field. Conceptually, I could generate a .ps1 file on startup given the current data set, and then load that ps1 as the module, but this feels a bit like killing a pup with a greatsword - lots of overkill. Is there a better option?
I had already implemented a similar function to the DynamicParam helper function, as reference in the comments. However, tab completion wasn't working. I was writing a minimal reproduction example, when...my tab completion worked.
It turns out, it reproducibly works/breaks based on the inclusion of a WriteDebug statement:
[Cmdlet("Get", "LivingCharacter")]
public class GetLivingCharacter : Cmdlet, IDynamicParameters
{
protected override void ProcessRecord()
{
}
public object GetDynamicParameters()
{
WriteDebug("Getting names"); // Tab completion won't work with this here - comment it out and it works.
^^^^^^^^^^
var chars = new List<String>() { "Bran", "Arya" };
var dict = new RuntimeDefinedParameterDictionary();
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a valid open name",
Mandatory = true
},
new ValidateSetAttribute(chars.ToArray()),
};
dict.Add("Name", new RuntimeDefinedParameter("Name", typeof(string), attributes));
return dict;
}
}
After some digging, the WriteDebug statement is throwing (which I assume is because it can't output while I'm typing). It then recreates the GetLivingCharacter class after I've finished the command to validate. It took a while to find since, because of the issue, I can't write the error to the console, so I had to append to a temp file instead.
On my form I have a button click
private void button1_Click(object sender, EventArgs e)
{
do something
}
How on the click would I load my do something from a text file, for example my text file looks like this:
MessageBox.Show("hello");
label1.Text = "Hello";
on click it does everything in my text file, if possible.
Here is a very simple example, just to prove this is possible. Basically, you use CodeDomProvider to compile source at runtime, then execute using reflection.
var provider = CodeDomProvider.CreateProvider("C#");
string src=#"
namespace x
{
using System;
public class y
{
public void z()
{
Console.WriteLine(""hello world"");
}
}
}
";
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
type.GetMethod("z").Invoke(instance, null);
}
Edit
As #Agat points out, the OP seems to require a sort of scripting framework (it makes use of label1, a property of the current object), whereas my answer above obviously does not provide that. The best I can think of is a limited solution, which would be to require dependencies to be specified explicitly as parameters in the "script". Eg, write the scripted code like this:
string src = #"
namespace x
{
using System.Windows;
public class y
{
public void z(Label label1)
{
MessageBox.Show(""hello"");
label1.Text = ""Hello"";
}
}
}
";
Now you can have the caller examine the parameters, and pass them in from the current context, again using reflection:
var result = provider.CompileAssemblyFromSource(new CompilerParameters(), src);
if (result.Errors.Count == 0)
{
var type = result.CompiledAssembly.GetType("x.y");
var instance = Activator.CreateInstance(type);
var method = type.GetMethod("z");
var args = new List<object>();
// assume any parameters are properties/fields of the current object
foreach (var p in method.GetParameters())
{
var prop = this.GetType().GetProperty(p.Name);
var field = this.GetType().GetField(p.Name);
if (prop != null)
args.Add(prop.GetValue(this, null));
else if (field != null);
args.Add(field.GetValue(this));
else
throw new InvalidOperationException("Parameter " + p.Name + " is not found");
}
method.Invoke(instance, args.ToArray());
}
Like the other answers have stated, it isn't an easy thing to implement and can possibly be done through reflection depending on how advanced your scripts are.
But no one #BrankoDimitrijevic mentioned Roslyn and it is a great tool. http://msdn.microsoft.com/en-us/vstudio/roslyn.aspx
It hasn't been updated in quite awhile (Sept.2012) and doesn't have all of the features of C# implemented, however, it did have a lot of it implemented when I played around with this release.
By adding your assembly as a reference to the scripting session, you're able to gain access to all of your assembly's types and script against them. It also supports return values so you can return any data that a scripted method generates.
You can find what isn't implemented here.
Below is a quick and dirty example of Roslyn that I just wrote and tested. Should work right out of box after installing Roslyn from NuGet. The small bloat at the initialization of the script engine can easily be wrapped up in a helper class or method.
The key is passing in a HostObject. It can be anything. Once you do, your script will have full access to the properties. Notice that you just call the properties and not the host object in the script.
Basically, your host object will contain properties of the data you need for your script. Don't necessarily think of your host object as just a single data object, but rather a configuration.
public class MyHostObject
{
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public class RoslynTest
{
public void Test()
{
var myHostObject = new MyHostObject
{
Value1 = "Testing Value 1",
Value2 = "This is Value 2"
};
var engine = new ScriptEngine();
var session = engine.CreateSession(myHostObject);
session.AddReference(myHostObject.GetType().Assembly.Location);
session.AddReference("System");
session.AddReference("System.Core");
session.ImportNamespace("System");
// "Execute" our method so we can call it.
session.Execute("public string UpdateHostObject() { Value1 = \"V1\"; Value2 = \"V2\"; return Value1 + Value2;}");
var s = session.Execute<string>("UpdateHostObject()");
//s will return "V1V2" and your instance of myHostObject was also changed.
}
}
No. You can not.
At least in any simple way.
The thing you want is something like eval('do something') from javascript.
That's not possible to do with C#. C# is a language which needs compilation before execution unlike javascript (for instance).
The only way to implement that is to build your own (pretty complicated as for beginner) parser and execute it in such way.
UPDATED:
Actually, as JDB fairly noticed, that's really not the only way. I love programming! There are so many ways to make a freakky (or even sometimes that really can be necessary for some custom interesting tasks (or even learning)!) code. he he
Another approach I've got in my mind is building some .cs file, then compiling it on-the-fly and working with it as some assembly or some other module. Right.
Suppose I am watching something in VS2008 and I want to search the object I'm watching for an instance of a particular value.
For example, say I'm watching this and I want to search for a control called mycontrol inside it or I want to search for a string that currently has the value of "Hello World".
I know it's kind of a brute force way of doing things, but I find it would be a quick way of identifying where things are going wrong whilst debugging. Warning: I'm about to swear... When I was using PHP, I could see all variables that were currently set by using get_defined_vars(). It was dirty, but it was quick and I'd like to know if there's something similar I can do in VS.
Sorry if I've been a little vague, I'd be happy to elaborate if you have questions.
Cheers
Iain
Edit:
What I'm actually tring to do is interrogate the current state of the application and quickly search for the various classes that I want to debug. What I'm doing is trying to debug where I don't have the source code (I'm writing a control for a CMS). So I know what the value of something should be, but I don't know where in the structure it exists - that's what I want to find.
e.g. An exeption is thrown by the application because foo should be a list of the type bar. I want to find out where foo is defined so I can look around and see what the other variables in the same class are set to.
Sorry again, I'm finding it hard to explain :(
Edit #2:
I find a good tree might help me visualise it better...
Quickwatch
-this
-var1
+var1a
+var1b
-var1c
-base
-foo = "Hello World"
+var1ca
+var2
+var3
In the above, how would I quickly drill down through the structure to find foo?
It sounds like you want a conditional breakpoint:
When the breakpoint location is
reached, the expression is evaluated
and the breakpoint is hit only if the
expression is true or has changed.
Create a new breakpoint, right-click on it, and select "Condition..." Enter the condition you'd like to wait for. It'll be something like:
this.MyString == "Hello World"
EDIT: Ok, I understand now you want to interrogate another, running application. Assuming it was built in a managed language, you may be interested in Hawkeye:
Hawkeye is the only .Net tool that
allows you to view, edit, analyze and
invoke (almost) any object from a .Net
application. Whenever you try to
debug, test, change or understand an
application, Hawkeye can help.
Free. Not been updated in a while.
I wrote this the other day. It did the job well enough (however it is only some utility code for debugging, so use at your own risk --> the design is pretty bad >_< ). Dumps out the fields and iterates downwards. It might not be perfect, but it solved my problem at the time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
public static class Dumper
{
public class Dump
{
public Dump(bool spacesInsteadOfTab)
{
_spacesIndeadOfTab = spacesInsteadOfTab;
}
private readonly StringBuilder _sb = new StringBuilder();
public string Result
{
get
{
return _sb.ToString();
}
}
private readonly bool _spacesIndeadOfTab;
private int _currentIndent;
public int CurrentIndent
{
get
{
return _currentIndent;
}
set
{
_currentIndent = value > 0 ? value : 0;
}
}
public void IncrementIndent()
{
CurrentIndent += 1;
}
public void DecrementIndent()
{
CurrentIndent -= 1;
}
private void AppendIndent()
{
if (_spacesIndeadOfTab)
_sb.Append(' ', _currentIndent * 4);
else
_sb.Append('\t', _currentIndent);
}
public void Log(string logValue)
{
AppendIndent();
_sb.AppendLine(logValue);
}
public void Log(string logValue, params object[] args)
{
AppendIndent();
_sb.AppendFormat(logValue, args);
_sb.AppendLine();
}
}
public static Dump TakeDump(object objectToDump, int maxDepth)
{
Dump result = new Dump(false);
int currentDepth = 0;
TakeDump(ref result, ref currentDepth, maxDepth, objectToDump);
return result;
}
private static void TakeDump(ref Dump result, ref int currentDepth, int maxDepth, object objectToDump)
{
currentDepth++;
if (currentDepth > maxDepth)
{
result.IncrementIndent();
result.Log("MaxDepth ({0}) Reached.", maxDepth);
result.DecrementIndent();
return;
}
var objectType = objectToDump.GetType();
result.Log("--> {0}", objectType.FullName);
result.IncrementIndent();
var fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Count() == 0)
result.Log("No fields");
foreach (var fieldInfo in fields)
{
var fieldValue = fieldInfo.GetValue(objectToDump);
if (fieldValue == null)
result.Log("{0} is null", fieldValueType.FullName, fieldInfo.Name);
var fieldValueType = fieldValue.GetType();
if (fieldValueType.IsValueType)
result.Log("{2} as {0} (ToString: {1})", fieldValueType.FullName, fieldValue.ToString(), fieldInfo.Name);
else
TakeDump(ref result, ref currentDepth, maxDepth, fieldValue);
}
result.DecrementIndent();
}
}
It sounds like you are envisioning a feature which would descend down the tree presented in the debugger UI looking for a typed in value. This is not a feature of the debugger at this time (although at first glance it sounds handy). It would have problems though in cases where the expression had infinite expansions.
Circular references for instance will cause an infinite expansion. Those are fairly easy to track down but there are more nefarious tricks which can be done to make infinite expansion harder / impossible to track. True we could probably control for depth and such ...
I think your best bet is to write a reflection based searching mechanism with a depth control mechanism. Then call this API from the debugger window.