Call method with class parameters through reflection - c#

I've just started out with C# reflection, and I ran into the issue of calling methods that take internal classes as arguments:
Sample class:
public class MyClass
{
public class CustomColor
{
public int h;
public int s;
public int v;
}
public string[] ConvertColors(List<CustomColor> colors)
{
//...
}
}
The code I'm using to call other methods in this class :
FieldInfo info = cInstanceContainerObject.GetType().GetField("_myClass", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance);
dynamic myClassObject = info.GetValue(cInstanceContainerObject);
Allowing me to do this :
myClassObject.SomeSampleMethod(int sampleParam);
However, I'm stuck on figuring out how to create the proper parameters for the ConvertColors method above. :(

I am not sure how you created your cInstanceContainerObject. I am using MethodInfo.Invoke. Below is the code:
//Your class
public class MyClass
{
public class CustomColor
{
public int h;
public int s;
public int v;
}
public string[] ConvertColors(List<CustomColor> colors)
{
return new string[]{"1"};
}
}
//Usage
MyClass mc = new MyClass();
MyClass.CustomColor cc = new MyClass.CustomColor();
Type t = mc.GetType();
MethodInfo mi = t.GetMethod("ConvertColors");
List<MyClass.CustomColor> lst = new List<MyClass.CustomColor>
{
new MyClass.CustomColor(),
new MyClass.CustomColor()
};
var x = (string[])mi.Invoke(mc,new object[]{lst});
Console.WriteLine (x.Count());

Tom,
Try passing the color you wish to convert as a reference.
In the example below, the color (CustomColor) you pass in as a reference will be updated, instead of returning strings.
So, instead of calling the method like:
string[] colors = ConvertColors(yourColorList, foo, bar);
it can instead be called as:
ConvertColors(yourColorList, foo, bar);
Example:
public class MyClass
{
public class CustomColor
{
public int h;
public int s;
public int v;
}
public enum Convert { H, S, V, HS, HV, SV, HSV };
public void ConvertColors(ref List<CustomColor> colors, Convert type,
ref CustomColor changeAmt)
{
// Variables to change H, S, and V from changeAmt parameter:
int changeH = changeAmt.h;
int changeS = changeAmt.s;
int changeV = changeAmt.v;
// Change the actual colors which were passed as 'colors' parameter.
switch (type)
{
// Change H.
case Convert.H:
foreach (CustomColor c in colors)
c.h += changeH;
break;
// Change S.
case Convert.S:
foreach (CustomColor c in colors)
c.s += changeS;
break;
// Change V.
case Convert.V:
foreach (CustomColor c in colors)
c.v += changeV;
break;
// Change HS.
case Convert.HS:
foreach (CustomColor c in colors)
{
c.h += changeH;
c.s += changeS;
}
break;
// Change HV.
case Convert.HV:
foreach (CustomColor c in colors)
{
c.h += changeH;
c.v += changeV;
}
break;
// Change SV.
case Convert.SV:
foreach (CustomColor c in colors)
{
c.s += changeS;
c.v += changeV;
}
break;
// Change HSV.
case Convert.HSV:
foreach (CustomColor c in colors)
{
c.h += changeH;
c.s += changeS;
c.v += changeV;
}
break;
}
}
}
Explanation:
'enum' allows you to create a custom parameter for your method:
public enum Convert { H, S, V, HS, HV, SV, HSV };
the 'switch' statement allows you to handle those parameters:
switch (type)
{
case Convert.H:
// Do something
return;
case Convert.S:
// Do something
return;
case Convert.V:
// Do something
return;
Create an instance of the changes you want to make as a new CustomColor, and pass a reference to the list of CustomColors, your custom parameter, and a reference to the changes as a CustomColor:
private void AnyMethod()
{
// Create first custom color.
CustomColor color1 = new CustomColor();
color1.h = 50;
color1.s = 25;
color1.v = 35;
// Create second custom color.
CustomColor color2 = new CustomColor();
color2.h = 50;
color2.s = 25;
color2.v = 35;
// Create third custom color.
CustomColor color3 = new CustomColor();
color3.h = 50;
color3.s = 25;
color3.v = 35;
// Add to list of custom colors.
List<CustomColor> colorList = new List<CustomColor>();
colorList.Add(color1);
colorList.Add(color2);
colorList.Add(color3);
// Create changes as a new color.
CustomColor colorChange = new CustomColor();
colorChange.h = -10;
colorChange.s = 47;
colorChange.v = -15;
// Update all colors in your list.
ConvertColors(ref colorList, Convert.HSV, ref colorChange); // BOOM.
}
If you wish to get the strings afterwards:
string[] hsvStrings =
{
color1.h.ToString(),
color1.s.ToString(),
color1.v.ToString(),
// Continue...
};

After a sleepless night I actually figured out how to achieve this. Both answers did help me see the possibilities that led me to the answer, however I had to achieve this without referencing the foreign DLL, or doing any modifications to it, just get an instance of it's main object through Reflection, and then call the various methods within it (some of which required classes only provided by the DLL itself as you can see in my example).
My solution is like this (feel free to provide better alternatives) :
Get a Type of both the List and CustomColor each, by utilizing the method parameter:
Type listType = myClassObject.GetType()
.GetMethod("ConvertColors")
.GetParameters()[0]
.ParameterType;
Type colorType = myClassObject.GetType()
.GetMethod("ConvertColors")
.GetParameters()[0]
.ParameterType
.GetProperty("Item")
.PropertyType;
Then create individual instances of both the entire list and individual CustomColors using Activator:
dynamic colorList = Activator.CreateInstance(listType);
dynamic customColor = Activator.CreateInstance(colorType);
colorList.Add(customColor);
myClassObject.ConvertColors(colorList); //works!

Related

How can I change field value with Mono Cecil?

I have a C# program and it has a class:
public class Test
{
internal const string a = "some value";
private DateTime b = new DateTime();
}
How can I use Mono Cecil to change their initial value so that it looks like this:
public class Test
{
internal const string a = "test";
private DateTime b = DateTime.MaxValue;
}
Right now I only have the following skeleton code and I don't know how to modify the fields.
void Main()
{
var input = #"C:\my_projects\a.exe";
var asm = AssemblyDefinition.ReadAssembly(input);
foreach (ModuleDefinition module in asm.Modules)
{
foreach (TypeDefinition type in module.GetTypes())
{
foreach (var field in type.Fields)
{
if (field.Name == "a")
{
}
else if (field.Name == "b")
{
}
}
}
}
asm.Write(#"c:\my_projects\b.exe");
}
Disclaimer
Very brittle code ahead
For the constant it is a matter of setting fld.Constant property.
For instance/static fields the C# compiler will emit the initialization code in the constructor, so you'll need to find the instruction that is loading the value that will be stored in the field and replace it.
using System.IO;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace x
{
class Program
{
public const string ConstValue = "old";
public int instanceField = 1;
static void Main(string[] args)
{
var o = new Program();
if (o.instanceField == 1)
{
using var a = AssemblyDefinition.ReadAssembly(typeof(Program).Assembly.Location);
var t = a.MainModule.Types.Single(ct => ct.Name == "Program");
var constant = t.Fields.First(f => f.Name == "ConstValue");
constant.Constant = "new value";
var ctor = t.Methods.Single(m => m.IsConstructor);
System.Console.WriteLine(ctor);
var il = ctor.Body.GetILProcessor();
var inst = il.Body.Instructions.First();
while (inst != null)
{
System.Console.WriteLine($"Checking {inst}");
if (inst.OpCode == OpCodes.Stfld && ((FieldReference) inst.Operand).Name == "instanceField")
{
// notice that this is very fragile. For this specific
// case I'd say it is safe but depending on how you
// initialize the field the instruction that loads
// the value to be assigned to the field may be located
// several instructions prior to the actual assignment
// but you can keep track of the stack state and figure
// out which instruction is pushing the value that
// will end up being poped into the field.
il.Replace(inst.Previous, il.Create(OpCodes.Ldc_I4, 42));
System.Console.WriteLine("Found");
break;
}
inst = inst.Next;
}
var p = typeof(Program).Assembly.Location;
var newBinaryPath = Path.Combine(Path.GetDirectoryName(p), Path.GetFileNameWithoutExtension(p) + "2" +Path.GetExtension(p));
a.Write(newBinaryPath);
System.Console.WriteLine($"New binary writen to {newBinaryPath}");
}
System.Console.WriteLine(o.instanceField);
// Notice that no matter what you do, this will always print the
// old value; that happens because the C# compiler will emit the
// constant at the call site (instead of referencing the field
// at runtime)
System.Console.WriteLine(ConstValue);
}
}
}

How to make a method which accept parameter or property like One in Annotaion Regular Expression

what is such method in the picture bellow and how can I make such one?
I searched internet a lot and did not find any thing. tried many thing like attribute method or property method bu nothing founded.
In-fact my problem is that I want to make a method to work like this and the input parameters be optional.
My Questions Image
this is my code bellow (Except panel others are enum type) :
public static void Reset(System.Windows.Forms.Panel panel, formulaType formulaType, ShalvarType shalvarType = 0
, DamanType damanType = 0, YaqeType yaqeType = 0, BalataneType balataneType = 0, AstinType astinType = 0)
{
object[,] collcetion = null;
switch (formulaType)
{
case formulaType.Shalvar:
collcetion = shalvarFurmula(shalvarType);
break;
case formulaType.Daman:
collcetion = damanFurmula(damanType);
break;
case formulaType.Yaqe:
collcetion = yaqeFurmula(yaqeType);
break;
case formulaType.Balatane:
collcetion = balataneFurmula(balataneType);
break;
case formulaType.Astin:
collcetion = astinFurmula(astinType);
break;
}
//System.Windows.Forms.TextBox
for (int i = 0; i < collcetion.Length; i++)
{
if (panel.Controls[collcetion[i, 0].ToString()].GetType().ToString() == "System.Windows.Forms.TextBox")
{
panel.Controls[collcetion[i, 0].ToString()].Text = collcetion[i, 1].ToString();
}
else
{
System.Windows.Forms.NumericUpDown num = panel.Controls[collcetion[i, 0].ToString()] as System.Windows.Forms.NumericUpDown;
num.Value = Convert.ToDecimal(collcetion[i, 1]);
}
}
}
I want to have panel and formula type but from the third one to end be like that. in fact I give the enum type the way like picture.
By the way the code i send is not complete yet.
Thankx
What your image refers to are an Attribute's optional properties, they are defined as:
public class MyAttribute : Attribute
{
public string SomeData { get; set; }
}
What you want is to use optional parameters. You achieve this like this:
public void DoSomething(string data = "", int age = 0) // data will be empty if no value is given
{
}
And you can then call this method with both:
DoSomething();
DoSomething("some data");
DoSomething(age: 10);

C# Array of references to Variables and or Form objects

What I am trying to do is to have an array of references to variables. By which I mean the equivalent to a C array of pointers to ints (for example).
Example: (!!not real code!!)
int a = 4;
int b = 5;
int c = 6;
List<ref int> tmp = new List<ref int>();
tmp.Add(ref a);
tmp.Add(ref b);
tmp.Add(ref c);
tmp[0] = 16;
tmp[1] = 3;
tmp[2] = 1000;
Console.Writeline(tmp[0] + " " + a); // 16 16
Console.Writeline(tmp[1] + " " + b); // 3 3
Console.Writeline(tmp[2] + " " + c); // 1000 1000
The specifics of my case: I have a list of strings that will correspond to the keys in a dictionary. What I think I want to have is a list of Tuples where Type1 is a reference to either an int or string, and Type2 is a reference to an Textbox.
I will be iterating through this list, using the string to get the value from the dictionary (and doing stuff with that data) then storing the results of that into Type1. And eventually I will be taking the data from those Type1 variable references and copying their data to the corresponding Type2 Textbox.
That's the gist of what I think I want to do. If someone thinks that my approach is overly complicated, I will say that I need to keep the Textboxes as they are sadly, so I can't just make an array and iterate through it. And it would be perferable to keep the Type1 variables seperate too, though not quite as necessary.
Now, from reading around, I thought Func<> looked like it was the closest thing to being useful for what I want, so I tried to use the following (with Type1, as an object because it needs to handle both ints and strings)
List<Tuple<string, Func<object>, Func<object>>>
but I was unsure how to use that to get references to the variables.
What you're specifically asking for isn't possible; what would be more appropriate (and has the convenience of actually working!) would be to design a class structure around what you're trying to do.
For instance, something like this:
public class MyObject // Name it appropriately
{
public object Value { get; set; }
public string Key { get; set; }
public TextBox TextBox { get; set; }
}
Then, in your code, you can do something akin to this...
Dictionary<string, object> values = ...
List<MyObject> objects = ...
foreach(var item in objects)
{
item.Value = values[item.Key];
// process your data
item.TextBox = item.Value.ToString();
}
Obviously, this is just a rough design and the class here serves as little more than a data transfer container. You could make the class "smarter" by, for example, using the setter for the Value property to set the value of the TextBox automatically. But this should hopefully give you the general idea of how something like this would be done in an OO fashion.
EDIT Here's how your example would look.
MyObject a = new MyObject() { Value = 4 };
MyObject b = new MyObject() { Value = 5 };
MyObject c = new MyObject() { Value = 6 };
List<MyObject> tmp = new List<MyObject>();
tmp.Add(a);
tmp.Add(b);
tmp.Add(c);
tmp[0].Value = 16;
tmp[1].Value = 3;
tmp[2].Value = 1000;
Console.Writeline(tmp[0].Value.ToString() + " " + a.Value.ToString()); // 16 16
Console.Writeline(tmp[1].Value.ToString() + " " + b.Value.ToString()); // 3 3
Console.Writeline(tmp[2].Value.ToString() + " " + c.Value.ToString()); // 1000 1000
You can't store references using C#. You can only use the ref keyword when calling a method.
You can use pointers, but you can only do that with a fixed expression and within an unsafe context.
It is possible to fake this kind of thing using delegates, but I'm not sure if it's what you're looking for. I'm also fairly sure that you really need to redesign your approach, but nevertheless, here's an example of how you can fake it...
Firstly, write a "value wrapper" class like so:
public class ValueWrapper<T>
{
readonly Func<T> get;
readonly Action<T> set;
public ValueWrapper(Func<T> get, Action<T> set)
{
this.get = get;
this.set = set;
}
public T Value
{
get
{
return get();
}
set
{
set(value);
}
}
}
Then you can use that to change values:
void run()
{
int x = 0;
var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
test(intWrapper);
Console.WriteLine(x); // Prints 42, which shows that x was changed.
TextBox textBox = new TextBox {Text = ""};
var stringWrapper = new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
test(stringWrapper);
Console.WriteLine(textBox.Text); // Prints "Changed".
}
static void test(ValueWrapper<int> wrapper)
{
wrapper.Value = 42;
}
static void test(ValueWrapper<string> wrapper)
{
wrapper.Value = "Changed";
}
You can also create a wrapper in one method and pass it to a different method which uses the wrapper to change a property in the original wrapped object, like so:
void run()
{
TextBox textBox = new TextBox {Text = ""};
var wrapper = test1(textBox);
test2(wrapper);
Console.WriteLine(textBox.Text); // Prints "Changed"
}
void test2(ValueWrapper<string> wrapper)
{
wrapper.Value = "Changed";
}
ValueWrapper<string> test1(TextBox textBox)
{
return new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
}
Warning: This does lead to some fairly head-scratching code, for example:
void run()
{
var intWrapper = test();
intWrapper.Value = 42;
Console.WriteLine(intWrapper.Value); // Works, but where is the value? It can't be the x inside test()!
}
ValueWrapper<int> test()
{
int x = 0;
var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
return intWrapper;
}
So we returned a ValueWrapper from test() which is apparently wrapping a local variable from inside test(). And then we can apparently change the value and print it out...
This isn't really what's happening, of course, but it can be quite confusing!
you can use pointers in this case, use unsafe keyword for method and set project unsafe to allow pointers in c#, also you can encapsulate the value in a class and in C# each class is of reference type
i used this and works perfect:
exp.
public int value1 = 3;
public int value2 = 4;
public int value3 = 5;
public void Method1()
{
int[] values = { value1, value2, value3};
for (int i = 0; i < values.Length; i ++)
{
Console.WriteLine(values[i]);
}
}

C#. Set a member object value using reflection

I need your help with the following code below. Basically I have a class called "Job" which has some public fields. I'm passing to my method "ApplyFilter" two parameters "job_in" and "job_filters". First parameter contains actual data, and the second one has instructions (if any). I need to iterate through "job_in" object, read it's data, apply any instructions by reading "job_filters", modify data (if needed) and return it in a new "job_out" object. Everything works fine till i need to store my data in "job_out" object:
public class Job
{
public string job_id = "";
public string description = "";
public string address = "";
public string details = "";
}
...
private Job ApplyFilters(Job job_in, Job job_filters)
{
Type type = typeof(Job);
Job job_out = new Job();
FieldInfo[] fields = type.GetFields();
// iterate through all fields of Job class
for (int i = 0; i < fields.Length; i++)
{
List<string> filterslist = new List<string>();
string filters = (string)fields[i].GetValue(job_filters);
// if job_filters contaisn magic word "$" for the field, then do something with a field, otherwise just copy it to job_out object
if (filters.Contains("$"))
{
filters = filters.Substring(filters.IndexOf("$") + 1, filters.Length - filters.IndexOf("$") - 1);
// MessageBox.Show(filters);
// do sothing useful...
}
else
{
// this is my current field value
var str_value = fields[i].GetValue(job_in);
// this is my current filed name
var field_name = fields[i].Name;
// I got stuck here :(
// I need to save (copy) data "str_value" from job_in.field_name to job_out.field_name
// HELP!!!
}
}
return job_out;
}
Please help. I've seen a few samples by using properties, but i'm pretty sure it is possible to do the same with fields as well. Thanks!
Try this
public static void MapAllFields(object source, object dst)
{
System.Reflection.FieldInfo[] ps = source.GetType().GetFields();
foreach (var item in ps)
{
var o = item.GetValue(source);
var p = dst.GetType().GetField(item.Name);
if (p != null)
{
Type t = Nullable.GetUnderlyingType(p.FieldType) ?? p.FieldType;
object safeValue = (o == null) ? null : Convert.ChangeType(o, t);
p.SetValue(dst, safeValue);
}
}
}
fields[i].SetValue(job_out, str_value);

How do I convert an array of structs into an array of Point3D objects in C#?

I intend to form 3D mesh object.
The mesh object has an 3D point array of approx. 50.000 items.
Due to the number of 3D points, the array must be initialized on the heap.
The required code is, shortly, as follows:
class MyMesh
{
public MeshGeometry3D Mesh3D // Properties tanimlaniyor
{
get { return GetMesh3D(); }
}
public struct mystruct
{
public int m_i;
public int m_j;
public int m_k;
public mystruct(int i, int j, int k)
{
m_i = i;
m_j = j;
m_i = k;
}
}
private mystruct[] mypts =
{
new mystruct(20 , 7 , 7),
.
.
new mystruct(23 , 5 , 7)
};
}
Could you explain me how 3D Coordinates in mystruct above can be converted
into 3D coordinates of a System.Windows.Media.Media3D.Point3D structure.
Thanks in advance.
Öner YILMAZ
If you have an actual list of 50,000 mystruct objects, would it be better to just create them as Point3D structs in the first place?
Simply do a "Find & Replace" of:
new mystruct(
and replace it with
new Point3D(
Then, change:
private mystruct[] mypts =
to:
private Point3D[] mypts =
Are you looking for something like this...
List<Point3D> points = mypts.Select<mystruct, Point3D> (x =>
new Point3D(x.m_i, x.m_j, x.m_k))
.ToList();
Alternatively, you could expose an iterator that returned an IEnumerable like this...
public IEnumerable<Point3D> Points()
{
foreach(var point in mypts)
{
yield return new Point3D(point.m_i, point.m_j, point.m_k, );
}
}
[add validation/error handling code as appropriate]
If you need to retain your mystruct objects as well as enable Point3D functionality, you could use something like:
class MyMesh {
...
public Point3D[] ToPoint3D()
{
Point3D[] p3D = null; // or set it to an Empty Point3D array, if necessary
if (mpts.Length > 0)
{
p3D = new Point3D[mpts.Length]
for (int x = 0; x < mypts.Length; x++)
{
p3D[x].X = new Point3D(mypts[x].m_i;
p3D[x].Y = new Point3D(mypts[x].m_j;
p3D[x].Z = new Point3D(mypts[x].m_k;
}
}
return p3D;
}
...
}

Categories