How to efficiently convert object values into an array? - c#

I have imported a very large CSV-table via Powershell into an array of objects.
Each object may look like this definition:
$test = [PsCustomObject]#{id='1';name='a'}
The problem is, that all types are 'string' and I need to work with the correct types later on in my code. For this I want to use an embedded C# code that converts the values of all object property into a string-array to add its values into a DataTable with correct type definitions.
I am now struggling on the C# part. Here is my code-sample which is not working.
How can I change the C# part to do the conversion of the object-values into an array?
# this code should convert the object-values into an array:
Add-Type -ReferencedAssemblies System.Data -TypeDefinition '
using System;
public class row {
public object[] row(object test) {
int id = Int32.Parse(test.id);
string name = test.name;
return (id, name);
}
}'
# this is the test-object:
$test = [PsCustomObject]#{id='1';name='a'}
# this is the final table with correct types:
$table = [System.Data.DataTable]::new()
[void]$table.Columns.Add('id', [int])
[void]$table.Columns.Add('name', [string])
# at the end it should work like this:
$row = [row]::new($test)
$table.Rows.Add($row)
I did some tests without C#, but this is very slow.
E.g. this simple loop (even without adding the data into the row) runs over 20 seconds:
$test = [PsCustomObject]#{id='1';name='a'}
foreach($x in 1..1MB) {
$row = foreach($i in $test.PsObject.Properties.Value){if ($i) {$i} else {$null}}
#[void]$table.rows.Add($row)
}
So in theory I need to do the same like in the last code-block but via embedded Csharp code.
How can I get this done in an efficient way?
Update #1:
Thanks to the input from Theo I could speed-up the conversion. I did not expect that this would be 5-times faster than just querying PsObject-properties. E.g. it turns out, that an 'else'-statement is slower than just assigning the var first. Here is the code for comparison:
$test = [PsCustomObject]#{id='1';name='a'}
foreach($x in 1..1MB) {
$id = $test.id
if ([string]::IsNullOrEmpty($id)){$id = $null}
$name = $test.name
$row = #($id, $name)
}
But it is still the slowest part in my overall code I am still looking for a smart C# solution. My idea is, that if there are any other properties for the input-object later, then I can just dynamically rebuild the C# code. That will not work for pure PS-code.
Update #2:
Based on the input from BACON I was able to solve the challenge with the C# code.
Here is my working implementation:
Add-Type -TypeDefinition '
using System;
public class test {
public string id {get;set;}
public string name {get;set;}
}
public static class DataParser {
public static string[] ParseToArray(test data) {
string id = data.id;
if (String.IsNullOrEmpty(id)) {id = null;};
string name = data.name;
return new string[] {id,name};
}
}'
# this is the test-object:
$test = [PsCustomObject]#{id='';name='a'}
$timer = [System.Diagnostics.Stopwatch]::StartNew()
foreach($x in 1..1MB) {
$row = [DataParser]::ParseToArray($test)
}
$timer.Stop()
$timer.Elapsed.TotalSeconds
What I did not expect is the runtime of this solution - it is way slower than the pure PowerShell version which I posted above. So my conclusion is "mission accomplished", but we missed the target. Which means that there is no real efficient way to convert object values into an array.
As a result of this, I will step away from importing CSV-data as objects and will focus on importing large data as XML via 'dataSet.readXML'. I only wish there would be a build-in option to directly import CSV-data as arrays or dataRows.

A pure PowerShell solution would be:
[int]$refInt = 0 # create an int as reference variable for TryParse()
foreach($item in $test) {
# get the int value or $null for the id property
$rowId = if ([int]::TryParse($item.id, [ref]$refInt)) { $refInt } else { $null }
# get the string value or $null for the name property
$rowName = $item.name.ToString() # added ToString() for good measure
if ([string]::IsNullOrWhiteSpace($rowName)) { $rowName = $null }
# add a new row to the table
$newRow = $table.NewRow()
$newRow["id"] = $rowId
$newRow["Name"] = $rowName
$null = $table.Rows.Add($newRow)
}
I'm not really into C#, but I think you need to use TryParse() there aswell in order to get either an int or a $null value. As for the name property you should also check this for NullOrWhiteSpace and use the ToString() method on it to make sure you get a valid string or $null.

You don't say how your C#-based attempt is "not working", but I can see some problems with...
using System;
public class row {
public object[] row(object test) {
int id = Int32.Parse(test.id);
string name = test.name;
return (id, name);
}
}
This is defining a class named row with an instance method (not a constructor) also named row. You don't define any constructors so the row class will have only a default, parameterless constructor. When you do...
$row = [row]::new($test)
...you are trying to invoke a row constructor overload that doesn't exist.
Further, the return type of row() is object[], yet (id, name) is a (value) tuple, not an array. Some conversion from the former to the latter is necessary for that to compile.
Invoking your Add-Type command I am reminded that...
Add-Type: (4,21): error CS0542: 'row': member names cannot be the same as their enclosing type
public object[] row(object test) {
^
...which explains itself, and...
Add-Type: (5,35): error CS1061: 'object' does not contain a definition for 'id' and no accessible extension method 'id' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)
...which means that since the compile-time type of the test parameter is object, unless you cast it to a more specific type you'll only be able to access the members of the object class, which has no properties or fields. Since the run-time type of test will be PSCustomObject — which is a bit of a "magical" type — the typical (and slow) C# way of dynamically accessing the id and name properties with reflection won't work.
So, the problem is, basically, that despite having some superficial similarities, C# is very different than PowerShell and cannot be written as such. You could fix the issues above by stuffing the values into a more friendly type for C# to access, like this...
using System;
public static class DataParser {
public static object[] ParseToArray(Tuple<string, string> data) {
int id = int.Parse(data.Item1);
string name = data.Item2;
return new object[] { id, name };
}
}
Note that Tuple<,> generic type is not the same kind of tuple as linked previously; that one requires C# 7.0, so, for better compatibility, I'm not using it here. You could then call the above method like this...
$testTuple = [Tuple]::Create($test.id, $test.name)
$testAsArray = [DataParser]::ParseToArray($testTuple)
$table.Rows.Add($testAsArray)
Even simpler would be to eliminate the intermediate object and just pass the properties via parameters...
using System;
public static class DataParser {
public static object[] ParseToArray(string id, string name) {
return new object[] { int.Parse(id), name };
}
}
...and call it like this...
$testAsArray = [DataParser]::ParseToArray($test.id, $test.name)
$table.Rows.Add($testAsArray)
Seeing as how neither method implementation is doing much more than stuffing their inputs into an array, the next — and best — optimization is to recognize that the C# code isn't doing enough work to justify its usage and remove it entirely. Thus, we just create the array directly in PowerShell...
$testAsArray = [Int32]::Parse($test.id), $test.name
$table.Rows.Add($testAsArray)
Now, that simplifies your code, but it doesn't achieve the goal of making it faster. As I said, you need to do more work inside the C# method — like accepting all of the input records, parsing them as appropriate, and populating the DataTable — to make it worthwhile. For that, I think you'd need to show more of your code; specifically, how you go from CSV text to in-memory records, and if each record really is stored as a PSCustomObject (as returned by Import-Csv) or something else.

Related

Parameter type validation in functions - binary modules vs. script modules

Having built a number of PowerShell modules (both binary and script), there's still an inconsistency between the two models that I cannot quite get my head around. Perhaps one of you could shed some light on this matter.
Imagine a function that accepts a single DateTime value. In a script function, I would define this as a [DateTime] parameter; in the C# function the parameter would be of type DateTime. So far, so good.
Now imagine passing a DateTime to the function, to which an additional note property has been added using Add-Member. Despite being defined as a [DateTime], the script function parameter would gladly accept this value, since it's actually a PSObject wrapping the original DateTime (and potentially containing additional members) which is unwrapped upon use - I hope I'm using the correct terminology here. As expected, passing something other than a (wrapped) DateTime would fail, thus making the function more or less type-safe.
The equivalent C# function defines the parameter as a DateTime, so AFAIK it has no way to access the additional parameter. After all, the only "interface" that the parameter provides comes from the DateTime type.
Alternatively, I could define the C# function's parameter type as a PSObject, but then I would have to do my own type checking for the PSObject's BaseObject.
Is there a flaw in my logic? Or, more importantly, is there a way around this, so that I can still leave my binary module's type checking to PowerShell?
Many thanks in advance!
You're both right and wrong - it entirely depends on whether the target parameter is of a value type (System.DateTime is a struct for example) - in which case everything is lost on type coercion during parameter binding.
If, however, the parameter type is of a reference type you can "resurrect" the PSObject wrapper using PSObject.AsPSObject().
I came up with the following example in pure(-ish) PowerShell for ease of replicability, but I believe it adequately shows my point
Paste the following into a C# source file (say, TestCmdlets.cs):
using System;
using System.Management.Automation;
namespace TestPSObject
{
// This will be our parameter type
public class TestObject {}
// This will be our reference type test cmdlet
[Cmdlet(VerbsDiagnostic.Test, "PSObjectByRef")]
public class TestPSObjectByRefCommand : Cmdlet
{
[Parameter(Mandatory=true)]
public TestObject TestObject
{
get { return testObject; }
set { testObject = value; }
}
private TestObject testObject;
protected override void ProcessRecord()
{
// If this works, we should receive an object with
// identical psextended properties
WriteObject(PSObject.AsPSObject(this.TestObject));
}
}
// This will be our value type test cmdlet
[Cmdlet(VerbsDiagnostic.Test, "PSObjectByValue")]
public class TestPSObjectByValueCommand : Cmdlet
{
[Parameter(Mandatory=true)]
public DateTime DateTime
{
get { return dateTime; }
set { dateTime = value; }
}
private DateTime dateTime;
protected override void ProcessRecord()
{
// If this works, we should receive an object with
// identical psextended properties (hint: we won't)
WriteObject(PSObject.AsPSObject(this.DateTime));
}
}
}
Now, in your shell, compile and import our test module:
Add-Type -Path .\TestCmdlets.cs -OutputAssembly TestPSObject.dll -OutputType Library
Import-Module .\TestPSObject.dll
Next up we create our test subjects and add a note property to them:
$TestObject = New-Object TestPSObject.TestObject
$TestObject |Add-Member -MemberType NoteProperty -Name TestProperty -Value "Hi there!"
$DateTime = Get-Date
$DateTime |Add-Member -MemberType NoteProperty -Name TestProperty -Value "Hi there!"
They now both return the string value Hi there! when you dereference the TestProperty member.
Now for the actual test:
$TestObjectAfter = Test-PSObjectByRef -TestObject $TestObject
$DateTimeAfter = Test-PSObjectByValue -DateTime $DateTime
This will still return Hi there!:
$TestObjectAfter.TestProperty
But this will not:
$DateTimeAfter.TestProperty

Is there a typed way to declare a method name in C#

I have some reflection code and I would love to have a way of binding the method names to types instead of declaring through strings.
I have this interface:
interface IDoStuff<T> {
void Do(T stuff);
}
Then I have this code:
object stuff = GotThisFromSomewhereElse();
object doer = GotThisFromSomewhereElseAlso();
var doMethodInfo = doer.GetType().GetMethod("Do");
doMethodInfo.Invoke(doer, new[] { stuff });
The problem is that I can't simply do a safe cast and call it because it's generic and I don't actually know what type T is.
This works fine but when I rename the method I have to go update this, I'm not overly concerned as I have tests to confirm all of this works which protects against not knowing it changed.
It's just really ugly and I was curious if there is some slick way to have this typed and thus will get renamed by ReSharper if I change it.
I'd really like something like:
object stuff = GotThisFromSomewhereElse();
object doer = GotThisFromSomewhereElseAlso();
var doMethodInfo = doer.GetType().Methods.Do;
doMethodInfo.Invoke(doer, new[] { stuff });
Thanks in advance and please let me know if this is something that is possible in C#.
Starting with C# 6, you'll be able to avoid the magic string using the new nameof statement:
IDoStuff<object> dummy = null; // don't need a valid instance.
string methodName = nameof(dummy.Do) // yay! no magic strings.
EDIT: #31eee384 pointed out in the comments that the above can be further simplified like this:
string methodName = nameof(IDoStuff<object>.Do);
About the new nameof statement, the documentation has this to say, which seems very much in line with what OP is trying to accomplish:
you often want to capture the string name of a method. Using nameof helps keep your code valid when renaming definitions. Before you had to use string literals to refer to definitions, which is brittle when renaming code elements because tools do not know to check these string literals.
Before C# 6, it's also possible to avoid magic strings by using expressions, but it's a little clunkier. Here is an example that would work in your case.
First, you write the following extension method:
public static string GetMethodName<T>(this T instance, Expression<Action<T>> methodExpression)
{
if (methodExpression.Body is MethodCallExpression)
{
return ((MethodCallExpression)methodExpression.Body).Method.Name;
}
else
{
throw new ArgumentException(string.Format("Invalid method expression: {0}", methodExpression.Body));
}
}
And then you can use it like this:
IDoStuff<object> dummy = null; // don't need a valid instance.
string methodName = dummy.GetMethodName(t => t.Do(null)); // yay! still no magic strings.
Create generic method DoIt:
private void DoIt<T>(T stuff, IDoStuff<T> doer) {
doer.Do(stuff);
}
and call it:
DoIt(GotThisFromSomewhereElse(), GotThisFromSomewhereElseAlso());
Of course, GotThisFromSomewhereElseAlso and GotThisFromSomewhereElse should be generics as well.

Can't write string from table to string attribute in C#

I read values from an local Access mdb-file. One value is stored as string in the db and I have it in a table. When using the GetType() method it return "System.String" and I can print it on the console without a problem but when I want to use it as an attribute for another method (requires a string) I get an error ("Cannot convert from 'object' to 'string'" and the same for 'int'). The same problems occur with some int values.
Am I doing something wrong or what is the problem in that case?
Console.WriteLine(dt.Rows[0][ProjName]); //prints project_name
Console.WriteLine(dt.Rows[0][ProjName].GetType()); //print "System.String"
Project = new Project(dt.Rows[0][ProjName], dt.Rows[0][MinDay], dt.Rows[0][MinWeek], dt.Rows[0][DayWeek]); //Error
Project = new Project(Convert.ToString(dt.Rows[0][ProjName]), Convert.ToInt32(dt.Rows[0][MinDay]), Convert.ToInt32(dt.Rows[0][MinWeek]), Convert.ToInt32(dt.Rows[0][DayWeek])); //Works Fine
Constructor for the Project Class:
public Project(string projectName, int hoursPerDay, int hoursPerWeek, int daysPerWeek)
You have stated in your answer is works when converting, and it is necessary as they are not strings and integers. They are objects. You can create a methid to handle it if you want.
public Project CreateProject(object projectName, object hoursPerDay, object hoursPerWeek, object daysPerWeek)
{
return new Project(projectName.ToString(), Convert.ToInt32(hoursPerDay), Convert.ToInt32(hoursPerWeek), Convert.ToInt32(daysPerWeek);
}
You have to explicitly cast the objects:
To cast to string use:
Object.ToString();
To cast to integers use:
Int32.TryParse(String, out int);
Your constuctor becomes
Project = new Project(dt.Rows[0][ProjName].ToString(), Int32.Parse(dt.Rows[0][MinDay]), Int32.Parse(dt.Rows[0][MinWeek]), Int32.Parse(dt.Rows[0][DayWeek]));
Note: Using Int32.Parse instead of Int32.TryParse assumes that the argument provided is a valid int at all times and does not give you a way to check if the casting has succeeded.
dt.Rows[0][ProjName] returns type object, and your method expects string. Even though you know it to be a string, it is not obvious to the compiler and must be specified explicitly using a cast, as you show in your last example, although just casting should be more efficient than converting unnecessarily:
Project = new Project((string)dt.Rows[0][ProjName], ...

Can I override ToString() for classes in a public API?

I'm writing a tool that interfaces with an API for another piece of software. Part of my tool will need to generate reports about the various objects found through the API, and I want these reports to contain simple strings that identify each object. By default I plan to use ToString() to generate the string for each object. However, not surprisingly I've found that the default ToString() implementations in this API aren't to descriptive.
Initially I was thinking of doing something like the code below with a long Switch statement. Although this would most likely become unmanageably long.
public string GetAPIObjectDescrition(object obj)
{
Type t = obj.GetType();
Switch(t)
{
Case typeof(SomeAPIType):
SomeAPIType x = (SomeAPIType)obj;
return x.SomeProperty;
Case typeof(SomeOtherAPIType):
SomeOtherAPITypex = (SomeOtherAPIType)obj;
return x.SomeOtherProperty;
default:
return x.ToString();
}
}
Next I tried using extension methods (see the code below). CustomObjectDescription() worked as expected, but when I tried to call ToString() it just returns the default ToString() results. I've never used extension methods before so I could be completely off base thinking something like this is even possible.
I can't guarantee that there will be a CustomObjectDescription() extension for every Type encountered in the API, so if I take this route I would end up having to use reflection each time to check if the current object has a GetObjectDescription() extension. I'd like to avoid using reflection if at all possible.
public static class APIObjectDescriptionExtensions
{
public static string ToString(this APIObject element)
{
return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
}
public static string CustomObjectDescription(this APIObject element)
{
return "ElementName = " + element.Name + " ElementID =" + element.Id.IntegerValue.ToString();
}
}
Does anyone have any other suggestions on how I should approach this problem? I'd prefer a solution where the code for each API Type is independent from one another (no giant Switch statement).
Also if possible I'd like the description string code for one type to inherit to sub types unless those types have their own unique description string code.
I think there might be a better solution that involves creating custom TypeConverters or maybe overriding/extending System.Convert.ToString()?
Update
I think the example below might help clarify what I'm trying to do. Ultimately I want to be able to take any arbitrary class from this API, the Type of which is not known until run time, and generate a description string. If the Type has my custom extension method then it should be used, otherwise the code should fall back on plain old ToString().
public static string GetDataDescription(object o)
{
//get the type of the input object
Type objectType = o.GetType();
//check to see if a description extension method is defined
System.Reflection.MethodInfo extensionMethod = objectType.GetMethod("MyDescriptionExtensionMethod");
if (extensionMethod != null)
{
//if a description extension method was found returt the result
return (string)extensionMethod.Invoke(o, new object[] { });
}
else
{
//otherwise just use ToString();
return o.ToString();
}
}
This code above doesn't work though because extension methods aren't found by GetMethod().
You could provide a wrapper for each of the classes similar to this:
public class SomeAPITypeWrapper : SomeAPIType
{
public override string ToString()
{
return SomeProperty;
}
}
public class SomeOtherAPITypeWrapper : SomeOtherAPIType
{
public override string ToString()
{
return SomeOtherProperty;
}
}
This certainly allows for using base classes/sub classes as requested in your question. It also keeps it clean and within your object model itself instead of in a switch statement or helper class.
Did you try using another name other then ToString() in your extension class? I am not completely sure about extension methods either, but I am guessing the base.ToString was getting called instead of yours. Possibly making a ToDescription() extension method would yield better results.
If a given method call can be resolved by an instance method and an extension method, the instance method is given preference. So extension methods need to be named such that they don't have same names as methods in the extended type.
From the code above, it seems that you don't control the source of APIObject and its derivations. So your options are 'Introduce Foreign Method' and 'Introduce Local Extension'
I'd try foreign method (which is similar to C# extension methods).. not sure why you would need reflection though. If the extension method doesn't exist, it'd be a compile-time error. How are you consuming this method ?
Finally switch statements are not that bad... unless they are very long/need frequent changes/duplicated across locations.
I suggest making a Dictionary<Type,Converter<object,string>>. Then you can look for a custom stringizer, and if none is found, call ToString.
Note, the dictionary will check for an exact match on types, so if you want to handle subclasses you'll have to write some additional code to see whether base types are listed in the dictionary (hint: if you match a base class, or even if you don't, go ahead and add the actual derived type to the dictionary so you won't have to recurse through the inheritance tree again).
Note that you can build an "open delegate" for Object.ToString() which conforms to the Converter<object,string> contract and use that as a default, even store it in the dictionary, instead of special-casing the call to ToString.
You could defer all tostringing to a separate concern of your application. StatePrinter (https://github.com/kbilsted/StatePrinter) is one such API where you can use the defaults or configure depending on types to print.
var car = new Car(new SteeringWheel(new FoamGrip("Plastic")));
car.Brand = "Toyota";
then print it
StatePrinter printer = new StatePrinter();
Console.WriteLine(printer.PrintObject(car));
and you get the following output
new Car() {
StereoAmplifiers = null
steeringWheel = new SteeringWheel()
{
Size = 3
Grip = new FoamGrip()
{
Material = ""Plastic""
}
Weight = 525
}
Brand = ""Toyota"" }
and with the IValueConverter abstraction you can define how types are printer, and with the FieldHarvester you can define which fields are to be included in the string.

How can I evaluate C# code dynamically?

I can do an eval("something()"); to execute the code dynamically in JavaScript. Is there a way for me to do the same thing in C#?
An example of what I am trying to do is: I have an integer variable (say i) and I have multiple properties by the names: "Property1", "Property2", "Property3", etc.
Now, I want to perform some operations on the " Propertyi " property depending on the value of i.
This is really simple with Javascript. Is there any way to do this with C#?
Using the Roslyn scripting API (more samples here):
// add NuGet package 'Microsoft.CodeAnalysis.Scripting'
using Microsoft.CodeAnalysis.CSharp.Scripting;
await CSharpScript.EvaluateAsync("System.Math.Pow(2, 4)") // returns 16
You can also run any piece of code:
var script = await CSharpScript.RunAsync(#"
class MyClass
{
public void Print() => System.Console.WriteLine(1);
}")
And reference the code that was generated in previous runs:
await script.ContinueWithAsync("new MyClass().Print();");
DISCLAIMER: This answer was written back in 2008. The landscape has changed drastically since then.
Look at the other answers on this page, especially the one detailing Microsoft.CodeAnalysis.CSharp.Scripting.
Rest of answer will be left as it was originally posted but is no longer accurate.
Unfortunately, C# isn't a dynamic language like that.
What you can do, however, is to create a C# source code file, full with class and everything, and run it through the CodeDom provider for C# and compile it into an assembly, and then execute it.
This forum post on MSDN contains an answer with some example code down the page somewhat:
create a anonymous method from a string?
I would hardly say this is a very good solution, but it is possible anyway.
What kind of code are you going to expect in that string? If it is a minor subset of valid code, for instance just math expressions, it might be that other alternatives exists.
Edit: Well, that teaches me to read the questions thoroughly first. Yes, reflection would be able to give you some help here.
If you split the string by the ; first, to get individual properties, you can use the following code to get a PropertyInfo object for a particular property for a class, and then use that object to manipulate a particular object.
String propName = "Text";
PropertyInfo pi = someObject.GetType().GetProperty(propName);
pi.SetValue(someObject, "New Value", new Object[0]);
Link: PropertyInfo.SetValue Method
Not really. You can use reflection to achieve what you want, but it won't be nearly as simple as in Javascript. For example, if you wanted to set the private field of an object to something, you could use this function:
protected static void SetField(object o, string fieldName, object value)
{
FieldInfo field = o.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic);
field.SetValue(o, value);
}
This is an eval function under c#. I used it to convert anonymous functions (Lambda Expressions) from a string.
Source: http://www.codeproject.com/KB/cs/evalcscode.aspx
public static object Eval(string sCSCode) {
CSharpCodeProvider c = new CSharpCodeProvider();
ICodeCompiler icc = c.CreateCompiler();
CompilerParameters cp = new CompilerParameters();
cp.ReferencedAssemblies.Add("system.dll");
cp.ReferencedAssemblies.Add("system.xml.dll");
cp.ReferencedAssemblies.Add("system.data.dll");
cp.ReferencedAssemblies.Add("system.windows.forms.dll");
cp.ReferencedAssemblies.Add("system.drawing.dll");
cp.CompilerOptions = "/t:library";
cp.GenerateInMemory = true;
StringBuilder sb = new StringBuilder("");
sb.Append("using System;\n" );
sb.Append("using System.Xml;\n");
sb.Append("using System.Data;\n");
sb.Append("using System.Data.SqlClient;\n");
sb.Append("using System.Windows.Forms;\n");
sb.Append("using System.Drawing;\n");
sb.Append("namespace CSCodeEvaler{ \n");
sb.Append("public class CSCodeEvaler{ \n");
sb.Append("public object EvalCode(){\n");
sb.Append("return "+sCSCode+"; \n");
sb.Append("} \n");
sb.Append("} \n");
sb.Append("}\n");
CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
if( cr.Errors.Count > 0 ){
MessageBox.Show("ERROR: " + cr.Errors[0].ErrorText,
"Error evaluating cs code", MessageBoxButtons.OK,
MessageBoxIcon.Error );
return null;
}
System.Reflection.Assembly a = cr.CompiledAssembly;
object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");
Type t = o.GetType();
MethodInfo mi = t.GetMethod("EvalCode");
object s = mi.Invoke(o, null);
return s;
}
I have written an open source project, Dynamic Expresso, that can convert text expression written using a C# syntax into delegates (or expression tree). Expressions are parsed and transformed into Expression Trees without using compilation or reflection.
You can write something like:
var interpreter = new Interpreter();
var result = interpreter.Eval("8 / 2 + 2");
or
var interpreter = new Interpreter()
.SetVariable("service", new ServiceExample());
string expression = "x > 4 ? service.SomeMethod() : service.AnotherMethod()";
Lambda parsedExpression = interpreter.Parse(expression,
new Parameter("x", typeof(int)));
parsedExpression.Invoke(5);
My work is based on Scott Gu article http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx .
All of that would definitely work. Personally, for that particular problem, I would probably take a little different approach. Maybe something like this:
class MyClass {
public Point point1, point2, point3;
private Point[] points;
public MyClass() {
//...
this.points = new Point[] {point1, point2, point3};
}
public void DoSomethingWith(int i) {
Point target = this.points[i+1];
// do stuff to target
}
}
When using patterns like this, you have to be careful that your data is stored by reference and not by value. In other words, don't do this with primitives. You have to use their big bloated class counterparts.
I realized that's not exactly the question, but the question has been pretty well answered and I thought maybe an alternative approach might help.
I don't now if you absolutely want to execute C# statements, but you can already execute Javascript statements in C# 2.0. The open-source library Jint is able to do it. It's a Javascript interpreter for .NET. Pass a Javascript program and it will run inside your application. You can even pass C# object as arguments and do automation on it.
Also if you just want to evaluate expression on your properties, give a try to NCalc.
You can use reflection to get the property and invoke it. Something like this:
object result = theObject.GetType().GetProperty("Property" + i).GetValue(theObject, null);
That is, assuming the object that has the property is called "theObject" :)
You also could implement a Webbrowser, then load a html-file wich contains javascript.
Then u go for the document.InvokeScript Method on this browser. The return Value of the eval function can be catched and converted into everything you need.
I did this in several Projects and it works perfectly.
Hope it helps
Uses reflection to parse and evaluate a data-binding expression against an object at run time.
DataBinder.Eval Method
I have written a package, SharpByte.Dynamic, to simplify the task of compiling and executing code dynamically. The code can be invoked on any context object using extension methods as detailed further here.
For example,
someObject.Evaluate<int>("6 / {{{0}}}", 3))
returns 3;
someObject.Evaluate("this.ToString()"))
returns the context object's string representation;
someObject.Execute(#
"Console.WriteLine(""Hello, world!"");
Console.WriteLine(""This demonstrates running a simple script"");
");
runs those statements as a script, etc.
Executables can be gotten easily using a factory method, as seen in the example here--all you need is the source code and list of any expected named parameters (tokens are embedded using triple-bracket notation, such as {{{0}}}, to avoid collisions with string.Format() as well as Handlebars-like syntaxes):
IExecutable executable = ExecutableFactory.Default.GetExecutable(executableType, sourceCode, parameterNames, addedNamespaces);
Each executable object (script or expression) is thread-safe, can be stored and reused, supports logging from within a script, stores timing information and last exception if encountered, etc. There is also a Copy() method compiled on each to allow creating cheap copies, i.e. using an executable object compiled from a script or expression as a template for creating others.
Overhead of executing an already-compiled script or statement is relatively low, at well under a microsecond on modest hardware, and already-compiled scripts and expressions are cached for reuse.
You could do it with a prototype function:
void something(int i, string P1) {
something(i, P1, String.Empty);
}
void something(int i, string P1, string P2) {
something(i, P1, P2, String.Empty);
}
void something(int i, string P1, string P2, string P3) {
something(i, P1, P2, P3, String.Empty);
}
and so on...
I was trying to get a value of a structure (class) member by it's name. The structure was not dynamic. All answers didn't work until I finally got it:
public static object GetPropertyValue(object instance, string memberName)
{
return instance.GetType().GetField(memberName).GetValue(instance);
}
This method will return the value of the member by it's name. It works on regular structure (class).
You might check the Heleonix.Reflection library. It provides methods to get/set/invoke members dynamically, including nested members, or if a member is clearly defined, you can create a getter/setter (lambda compiled into a delegate) which is faster than reflection:
var success = Reflector.Set(instance, null, $"Property{i}", value);
Or if number of properties is not endless, you can generate setters and chache them (setters are faster since they are compiled delegates):
var setter = Reflector.CreateSetter<object, object>($"Property{i}", typeof(type which contains "Property"+i));
setter(instance, value);
Setters can be of type Action<object, object> but instances can be different at runtime, so you can create lists of setters.
Unfortunately, C# doesn't have any native facilities for doing exactly what you are asking.
However, my C# eval program does allow for evaluating C# code. It provides for evaluating C# code at runtime and supports many C# statements. In fact, this code is usable within any .NET project, however, it is limited to using C# syntax. Have a look at my website, http://csharp-eval.com, for additional details.
the correct answer is you need to cache all the result to keep the mem0ry usage low.
an example would look like this
TypeOf(Evaluate)
{
"1+1":2;
"1+2":3;
"1+3":5;
....
"2-5":-3;
"0+0":1
}
and add it to a List
List<string> results = new List<string>();
for() results.Add(result);
save the id and use it in the code
hope this helps

Categories

Resources