Get Reference to Field from Reflection [duplicate] - c#

This question already has answers here:
How to create a reference to a value-field
(4 answers)
Closed 2 years ago.
I'm working on an OpenGL game engine as a passion project and im using the UI library "Dear ImGUI" to display and debug values similar to Unity's inspector. I'm having trouble thinking of a way to get a reference to the field I'm trying to debug.
Here is the code i have got at the moment but the problem is that its not a reference to the actual field, its just a reference to a local variable (value) and as such, it doesnt actually set the variable that I debug in the GUI. From what i've been able to see, there is no clean cut way to get the reference.
protected override void OnImGUIRender(FrameEventArgs e)
{
ImGui.PushFont(font);
ImGui.ShowDemoWindow();
//Scene Window
{
ImGui.Begin("Scene");
ImGui.BeginTabBar("1");
ImGui.BeginTabItem("Heirachy");
if (ImGui.TreeNode("Scene"))
{
foreach (var obj in (LayerStack.Layers.FirstOrDefault(x => x.GetType() == typeof(GameLayer)) as GameLayer).scene.GameObjects)
{
if (ImGui.Selectable(obj.Name))
selectedGameObject = obj;
}
ImGui.TreePop();
}
ImGui.EndTabItem();
ImGui.EndTabBar();
ImGui.Dummy(new System.Numerics.Vector2(0, 40));
ImGui.BeginTabBar("Properties");
ImGui.BeginTabItem("Properties");
if (selectedGameObject != null)
{
ImGui.Text(selectedGameObject.Name);
foreach (var c in selectedGameObject.components)
{
if (ImGui.TreeNode(c.GetType().Name))
{
var fields = c.GetType().GetFields();
foreach (var field in fields)
{
ImGui.DragFloat3(field.Name, field.refValue); <-- Focused line
}
ImGui.TreePop();
}
}
}
else
ImGui.Text("No Currently Selected Gameobject");
ImGui.EndTabItem();
ImGui.EndTabBar();
ImGui.End();
ImGui.Begin("Debug");
ImGui.Text("Gameobjects: " + LayerStack.GameObjectCount);
ImGui.End();
}
base.OnImGUIRender(e);
}
Is there any way I can get a reference to the actual field that is being looped over in the foreach? In my head I would imagine it looking something like this:
ImGui.DragFloat3(field.Name, field.Reference);
Thanks!
Edit:
I found my personal solution to be in the code below but massive thanks to #pinkfloydx33 for helping me to understand the problem better and provide a high quality answer.
var fields = c.GetType().GetFields();
foreach (var field in fields)
{
var value = (field.FieldType)field.GetValue(c);
ImGui.DragFloat3(field.Name, field.refValue);
field.SetValue(c, value);
}

Part of the problem you are experiencing is due to the fact those field values are structs. You only end up operating on copies of them. But we can get around this by building a delegate that accepts as its only parameter an object of the containing type (the type who's fields you are inspecting). This delegate will in turn call the method you are trying to invoke, passing the object's field under the hood with ref.
This solution below assumes that the methods you want to invoke (ImGui.Drag3, ImGui.Checkbox) always have two parameters -- string name and ref T value. In other words, a hypothetical method that operated on int fields would have to be declared as ImGui.DoSomethingToInt(string name, ref int value)
using System.Linq.Expressions;
using System.Reflection;
using System.Collection.Generic;
public static class ComponentHelpers
{
// helper function to get the MethodInfo for the method we want to call
private static MethodInfo GetStaticMethod(Expression<Action> expression)
{
if (expression.Body is MethodCallExpression body && body.Method.IsStatic)
return body.Method;
throw new InvalidOperationException("Expression must represent a static method");
}
// helper field we can use in calls to GetStaticMethod
private static class Ref<T>
{
public static T Value;
}
// Define which method we want to call based on the field's type
// each of these methods must take 2 parameters (string + ref T)
private static readonly Dictionary<Type, MethodInfo> Methods = new Dictionary<Type, MethodInfo>
{
[typeof(Vector3)] = GetStaticMethod(() => ImGui.Drag3(default, ref Ref<Vector3>.Value)),
[typeof(bool)] = GetStaticMethod(() => ImGui.Checkbox(default, ref Ref<bool>.Value))
};
// store the compiled delegates so that we only build/compile them once
private static readonly Dictionary<FieldInfo, Action<Component>> Delegates = new Dictionary<FieldInfo, Action<Component>>();
// this method will either build us a delegate, return one we've already built
// or will return null if we have not defined a method for the specific type
public static Action<Component> GetActionFor(FieldInfo field)
{
if (!Methods.TryGetValue(field.FieldType, out var method))
return null;
if (Delegates.TryGetValue(field, out var del))
return del;
// type the parameter as the base class Component
var param = Expression.Parameter(typeof(Component), "x");
var lambda = Expression.Lambda<Action<Component>>(
Expression.Call(
method,
// Pass the field's name as the first parameter
Expression.Constant(field.Name, typeof(string)),
// pass the field as the second parameter
Expression.Field(
// cast to the actual type so we can access fields of inherited types
Expression.Convert(param, field.ReflectedType),
field
)
),
param
);
return Delegates[field] = lambda.Compile();
}
}
Once we've done that, we can update your main loop to look like the following:
var fields = c.GetType().GetFields();
foreach (var field in fields)
{
var action = ComponentHelpers.GetActionFor(field);
if (action == null) // no method defined
continue;
// invoke the function passing in the object itself
action(c);
}

Related

How to instantiate a Lazy<Square> using runtime type info, and a function that only returns Shape?

I want to create a Lazy<> with runtime information about the contained type, but I'm not sure how to create the required Func<> initializer. I feel the answer is somewhere in Delegate.CreateDelegate, but I couldn't see how to make that work. The below illustrates what I want to do:
class ShapeTools {
abstract class Shape {}
class Square : Shape {}
Lazy<Square> aLazyShape;
ShapeTools() {
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented)
Shape GetShape(Type shapeType) { return null; }
void setup (FieldInfo field) { // aLazyShape
var funcType = typeof(Func<>).MakeGenericType(field.FieldType); // = typeof(Func<Square>)
var shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
var lazy = Activator.CreateInstance(field.FieldType, myFunc); // This takes a Func<Square>
field.SetValue(this, lazy);
}
}
You can achieve what you're looking for by using System.Linq.Expressions. Why you want to do it this way is another thing. =)
See this other answer for where the key parts about using Expression came from.
Note that your code for creating var funcType was not returning typeof(Func<Square>), but instead typeof(Func<Lazy<Square>>); I fixed that. Most things were made public for convenience when compiling. You could change the access to GetShape if you want to update BindingFlags in the call to GetMethod, for example.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace SomeNamespace
{
class Program
{
static void Main(string[] args)
{
ShapeTools st = new ShapeTools();
ShapeTools.Square s = st.aLazyShape.Value;
// s will be whatever ShapeTools.GetShape returns
}
public class ShapeTools
{
public abstract class Shape { }
public class Square : Shape { }
public Lazy<Square> aLazyShape;
public ShapeTools()
{
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented, just an example)
public static object GetShape(Type shapeType) { return new Square(); }
void setup(FieldInfo field)
{ // only handles 'aLazyShape' right now
Type funcType = typeof(Func<>).MakeGenericType(field.FieldType.GenericTypeArguments[0]); // = typeof(Func<Square>)
Type shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// get MethodInfo for the static method in this class that returns the right shape
MethodInfo getInstanceOfType = GetType().GetMethod(nameof(GetShape));
// build the Func dynamically
var typeConst = Expression.Constant(shapeType); // get the shapeType as an Expression
var callGetInstance = Expression.Call(getInstanceOfType, typeConst); // invoke our (static) method to get the instance of shape
var cast = Expression.Convert(callGetInstance, shapeType); // cast the return of our method (which is object) to the right type
var toLambda = Expression.Lambda(cast); // wrap everything in a Lambda to return our instance
var finalFunc = toLambda.Compile(); // compile to the final Func
var lazy = Activator.CreateInstance(field.FieldType, finalFunc); // now create the Lazy<T>, where T is Square
field.SetValue(this, lazy);
}
}
}
}
Lastly, notice that GetShape was made static. This was for convenience when using Expressions - if you want, you can pass in an instance of ShapeTools to the Expressions code instead.
And as written, ShapeTools.setup is just an example of how this can work. I assume you'd want to clean it up to handle other field types than just Lazy<Shape>.
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
C# is a statically typed language, and as such every type has to be fully known at compile time. As you discovered, while you can ask the runtime to create you any type you build the definition for (with a Type object), the return type has to be fully known -- the function you call returns object for that very reason.
You have a few options here:
Stay on the object level, and use further reflection calls to consume your newly created object. Large performance hit, of course.
Go dynamic, which creates a compiler site to translate any calls through it. It also spreads like a plague, anything that uses dynamic returns dynamic, so until you get something you know the type of you'll be using this kind of calls. Amortized moderate performance hit, very high performance hit during the first JIT compilation.
Use interfaces. You have a base class already, if you define it smart enough, that should be all you need to use your object. That myfunc function would be of type Func<Shape> in that case, and it obviously can only access the Shape portion of your object, though you're free to try casting using is/as as needed, usually in ifs. Very low performance hit, but you need to design your type smart (which you should be doing anyway).

Xamarin forms duplicate object (deep copy)

I try to use Xamarin Forms (with PCL project). My scope is to make an app to consume public service (exposed via web services).
I try to use TODOASMX Example of Xamarin website. The problem is the code:
static TodoItem FromASMXServiceTodoItem (ASMXService.TodoItem item)
{
return new TodoItem {
ID = item.ID,
Name = item.Name,
Notes = item.Notes,
Done = item.Done
};
}
scope of this code is to copy data from ASMX web Service (ASMXService.TodoItem ) to private domanin (TodoItem). The types are identical, but on namespace different and than the type are different.
In my case the type TodoItem is more, more , more complicated and I need to use a deep copy.
Now I try to use this code for deep copy:
public static object CloneObject(object objSource)
{
//step : 1 Get the type of source object and create a new instance of that type
Type typeSource = objSource.GetType();
object objTarget = Activator.CreateInstance(typeSource);
//Step2 : Get all the properties of source object type
PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Step : 3 Assign all source property to taget object 's properties
foreach (PropertyInfo property in propertyInfo)
{
//Check whether property can be written to
if (property.CanWrite)
{
//Step : 4 check whether property type is value type, enum or string type
if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
{
property.SetValue(objTarget, property.GetValue(objSource, null), null);
}
//else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
else
{
object objPropertyValue = property.GetValue(objSource, null);
if (objPropertyValue == null)
{
property.SetValue(objTarget, null, null);
}
else
{
property.SetValue(objTarget, CloneObject(objPropertyValue), null);
}
}
}
}
return objTarget;
}
but when run the code the error is:
System.MissingMethodException: Default constructor not found for type TodoASMX.Droid.MeginetOTA.excInfoByLang[]
Now the type TodoASMX.Droid.MeginetOTA.excInfoByLang[] is not modificable for me and I cannot add default constructor to this type. This type is returned by import of public WebService.
Any workaround (or solution) is appreciated.
Very thanks in advance.
MP
The primary problem is that TodoASMX.Droid.MeginetOTA.excInfoByLang[] is an array. A array has no parameterless constructor, because you need to pass the length of it.
You have to handle arrays separately:
if (typeSource.IsArray)
{
var sourceArray = (Array) objSource;
var length = sourceArray.Length;
var copyArray = (Array)Activator.CreateInstance(typeSource, length);
for (var i = 0; i < length; i++)
{
var value = sourceArray.GetValue(i);
copyArray.SetValue(value, i);
}
}
You approach is maybe a bit complicated. Have a look at https://stackoverflow.com/a/78612/1489968 or search for a already implemented generic clone library.

Return value, Ref, Out is there any difference? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am doing this,
public Order Add(Order order)
{
order.thisA = GetValue1();
// update the state of object
return order;
}
Would i be any beneficial if I use Ref or Out here instead ?
Further implementation would require me to add this method,
public Order[] UpdateCollection(Order[] orderCollection)
{
foreach(Order o in orderCollection)
o = Update(o);
return orderCollection;
}
Please tell me in light of best practices as well.
Context:
At the moment I am only returning INT id of the order but in future maybe I need to return more properties, but here is the full context,
https://codereview.stackexchange.com/questions/97778/crud-operation-class#
In this case:
public MyObject Update(MyObject object)
{
object.thisA = GetValue1();
// update state of object
return object;
}
You are not changing MyObjects reference so:
You don't need to return the object back.
You don't need to use ref.
You don't need to use out.
Using out is for initializing an object (You must assign a value in the function).
MyObject obj; // didn't assign anything
Method(out obj);
public void Method(out MyObject obj){
obj = new MyObject(); // assigned
}
using ref is in case you might change the reference of the object inside the method:
MyObject obj = new MyObject();
Update(ref obj);
public void Update(ref MyObject obj)
{
obj = new MyObject(); // changing the ref!!!
obj.thisA = GetValue1();
}
BTW, in your case you don't need to do anything:
public void UpdateCollection(Order[] orderCollection)
{
foreach(Order o in orderCollection)
Update(o);
}
public void Update(MyObject object)
{
object.thisA = GetValue1();
}
You are foreaching an array and updating the object and not the reference.
You use ref if there is a chance that you want to change the reference instead of the object's data.
Example:
public void RetargetReference(ref List<string> originalList)
{
originalList = new List<string>();
originalList.Add("World");
}
List<string> inList = new List<string>();
inList.Add("Hello");
RetargetReference(ref inList);
This will change the reference of inList. It will now point to a new list which contains one entry "World". The list that contained "Hello" will no longer be available to you unless you have another reference to it.
ref parameters can be useful if you want to change parameter you passed in during the execution of the method.
out will be used to have the method create a new object instance without you being able to pass a value in!
Example:
public void CreateReference(out List<string> newList)
{
newList = new List<string>();
newList.Add("Hello World");
}
List<string> list;
CreateReference(out list);
After that, list will point to a new List<string> instance. Inside the method you have no access to whatever newList actually "points to". You will always have to create a new instance.
out parameters can be useful if you want your method to return more than one result. For example, the following method would return a bool to indicate success and two out parameters that contain the data:
public bool TrySplitString(string source, out string part1, out string part2)
{
part1 = String.Empty;
part2 = String.Empty;
string[] parts = source.Split('=');
if (parts.Length != 2)
return false;
part1 = parts[0];
part2 = parts[1];
return true;
}
Objects are generally passed by reference in C#, so the following method actually changes the data "outside" the method:
public void ChangeList(List<string> list)
{
list.Add("World");
}
List<string> inList = new List<string>();
inList.Add("Hello");
ChangeList(inList);
After that, inList contains two entries: "Hello" and "World".
The reason why sometimes you return the object that was passed in as a parameter is that this allows so called "method chaining" where you can "write sentences" instead of "commands". You'll see that in my next example:
public static List<string> CreateList()
{
return new List<string>();
}
public static List<string> AddItem(this List<string> list, string item)
{
list.Add(item);
return list;
}
public static List<string> DoSomethingWithList(this List<string> list)
{
...;
return list;
}
You can use this code and write something like this:
List<string> list = CreateList().AddItem("Hello").DoSomethingWithList();
Return object back may be used for create "method chaining", like this:
someObject.Update(MyObject object).MyObjectMethod1().MyObjectMethod2();
I can't really workout what you really trying to achieve. However, i will give you some basics.
In C#, arguments can be passed to parameters either by value or by reference.
If your MyObject is a reference type (eg: class) then it is passed to function as reference and hence you will end up in modifying same object. In other way, any change you make on this instance (within the function) will have a direct impact on that instance and it will continue to be true in the current execution environment.
On the other hand, if MyObject is a value type (eg: struct) then a copy of it will be passed to method. That is, original instance of your value type is not altered even if you modify the members within the method. This is default behavior when it comes to value type.
Now assume a scenario where you wanted to modify the original instance of value type. This can be achieved only if you pass the reference of your value type. Here comes ref and out. Using these keyword, you can pass parameter by reference. There are differences between ref and out, which can be learned separately. However, keeping your context in mind, the ref and out both allow the called method to modify a parameter.
ref means that the parameter has a value on it before going into the function. So that the function can read and or change the value within it. On the other hand, out means that the parameter has no official value before going into the function. The called function must initialize it before parameter goes out from function.

Reflection & Parameters in C#

I'm writing an application that runs "things" to a schedule.
Idea being that the database contains assembly, method information and also the parameter values. The timer will come along, reflect the method to be run, add the parameters and then execute the method.
Everything is fine except for the parameters.
So, lets say the method accepts an ENUM of CustomerType where CustomerType has two values of CustomerType.Master and CustomerType.Associate.
EDIT
I don't know the type of parameter that will be getting passed in. ENUM used as an example
END OF EDIT
We want to run Method "X" and pass in parameter "CustomerType.Master". In the database, there will be a varchar entry of "CustomerType.Master".
How do I convert the string "CustomerType.Master" into a type of CustomerType with a value of "Master" generically?
Thanks in advance,
Jim
OK, the scope of the question shifted but my original observation and objection to some other solutions still stands.
I think you don't/can't want to use 'generics' here. You don't know the type ahead of time, and since you will need to create the type, there is no need to use a generic implementation because MethodBase.Invoke takes an array of Object.
This code assumes you are instantiating the target from database field. If not just adjust accordingly.
Of course this is not all encompassing and has no useful exception handling, but it will allow you to dynamically execute arbitrary methods on an arbitrary type with arbitrary parameters values all coming from string values in a row.
NOTE: there are many many many scenarios in which this simple executor will not work. You will need to ensure that you engineer your dynamic methods to cooperate with whatever strategy you do end up deciding to use.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Reflection;
using NUnit.Framework;
namespace DynamicMethodInvocation
{
[TestFixture]
public class Tests
{
[Test]
public void Test()
{
// from your database
string assemblyQualifiedTypeName = "DynamicMethodInvocation.TestType, DynamicMethodInvocation";
string methodName = "DoSomething";
// this is how you would get the strings to put in your database
string enumString = Executor.ConvertToString(typeof(AttributeTargets), AttributeTargets.Assembly);
string colorString = Executor.ConvertToString(typeof(Color), Color.Red);
string stringString = "Hmm... String?";
object result = Executor.ExecuteMethod(assemblyQualifiedTypeName, methodName,
new[] { enumString, colorString, stringString });
Assert.IsInstanceOf<bool>(result);
Assert.IsTrue((bool)result);
}
}
public class TestType
{
public bool DoSomething(AttributeTargets #enum, Color color, string #string)
{
return true;
}
}
public class Executor
{
public static object ExecuteMethod(string assemblyQualifiedTypeName, string methodName,
string[] parameterValueStrings)
{
Type targetType = Type.GetType(assemblyQualifiedTypeName);
MethodBase method = targetType.GetMethod(methodName);
ParameterInfo[] pInfo = method.GetParameters();
var parameterValues = new object[parameterValueStrings.Length];
for (int i = 0; i < pInfo.Length; i++)
{
parameterValues[i] = ConvertFromString(pInfo[i].ParameterType, parameterValueStrings[i]);
}
// assumes you are instantiating the target from db and that it has a parameterless constructor
// otherwise, if the target is already known to you and instantiated, just use it...
return method.Invoke(Activator.CreateInstance(targetType), parameterValues);
}
public static string ConvertToString(Type type, object val)
{
if (val is string)
{
return (string) val;
}
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable to string");
}
return tc.ConvertToString(null, CultureInfo.InvariantCulture, val);
}
public static object ConvertFromString(Type type, string val)
{
TypeConverter tc = TypeDescriptor.GetConverter(type);
if (tc == null)
{
throw new Exception(type.Name + " is not convertable.");
}
if (!tc.IsValid(val))
{
throw new Exception(type.Name + " is not convertable from " + val);
}
return tc.ConvertFrom(null, CultureInfo.InvariantCulture, val);
}
}
}
I would think you have 2 major options:
Store the type name along with the parameter value and use that to cast things using Type.GetType(string) to resolve the type in question.
Standardize all the methods to be called this way to accept an array of strings, and expect the methods to do any necessary casting.
I know you've stated that you're not doing option 1, but it would help things from the standpoint of calling the functions.
Option 2 is the far more 'generic' way to handle the situation, assuming all values can be represented by and cast/converted from strings to the appropriate type. Of course, that only helps if you actually have control over the definition of the methods being called.
Below is a useful extension method I use in .NET 3.5.
With this extension method available, your code could look like this:
var valueInDb = GetStringFromDb().Replace("CustomerType.", string.Empty);
var value = valueInDb.ToEnum(CustomerType.Associate);
By supplying the default value in the parameter, the compiler will know which Enum you want your string to be turned into. It will try to find your text in the Enum. If it doesn't it will return the default value.
Here is the extension method: (this version also does partial matches, so even "M" would work nicely!)
public static T ToEnum<T>(this string input, T defaultValue)
{
var enumType = typeof (T);
if (!enumType.IsEnum)
{
throw new ArgumentException(enumType + " is not an enumeration.");
}
// abort if no value given
if (string.IsNullOrEmpty(input))
{
return defaultValue;
}
// see if the text is valid for this enumeration (case sensitive)
var names = Enum.GetNames(enumType);
if (Array.IndexOf(names, input) != -1)
{
// case insensitive...
return (T) Enum.Parse(enumType, input, true);
}
// do partial matching...
var match = names.Where(name => name.StartsWith(input, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if(match != null)
{
return (T) Enum.Parse(enumType, match);
}
// didn't find one
return defaultValue;
}
I still don't fully understand your question... however, you say "Everything is fine except for the parameters."
I'll assume "CustomerType" the name of a property on your object, and "Master" is the string value you want to put in that property.
Here is (another) extension method that may help.
Once you have your new object and the value and property name from the database field, you could use this:
// string newValue = "Master";
// string propertyName = "CustomerType";
myNewObject.SetPropertyValue(propertyName, newValue)
Method:
/// <summary>Set the value of this property, as an object.</summary>
public static void SetPropertyValue(this object obj,
string propertyName,
object objValue)
{
const BindingFlags attr = BindingFlags.Public | BindingFlags.Instance;
var type = obj.GetType();
var property = type.GetProperty(propertyName, attr);
if(property == null) return;
var propertyType = property.PropertyType;
if (propertyType.IsValueType && objValue == null)
{
// This works for most value types, but not custom ones
objValue = 0;
}
// need to change some types... e.g. value may come in as a string...
var realValue = Convert.ChangeType(objValue, propertyType);
property.SetValue(obj, realValue, null);
}
If you are using .NET 4 you can do the following.
var result = default(CustomerType);
if (!Enum.TryParse("Master", out result))
{
// handle error
}

C# using reflection to create a struct

I am currently writing some code to save general objects to XML using reflection in c#.
The problem is when reading the XML back in some of the objects are structs and I can't work out how to initialise the struct. For a class I can use
ConstructorInfo constructor = SomeClass.GetConstructor(Type.EmptyTypes);
however, for a struct, there is no constructor which takes no parameters so the above code sets constructor to null. I also tried
SomeStruct.TypeInitializer.Invoke(null)
but this throws a memberaccessexception. Google gives no promising hits. Any help would be appreciated.
If the values are structs, they're likely to be immutable - so you don't want to call a parameterless constructor, but the one which takes the appropriate values as constructor arguments.
If the structs aren't immutable, then run away from them as fast as possible, if you can... but if you absolutely have to do this, then use Activator.CreateInstance(SomeClass). You'll have to be very careful when you use reflection to set properties or fields on the value type though - without that care, you'll end up creating a copy, changing the value on that copy, and then throwing it away. I suspect that if you work with a boxed version throughout, you'll be okay:
using System;
// Mutable structs - just say no...
public struct Foo
{
public string Text { get; set; }
}
public class Test
{
static void Main()
{
Type type = typeof(Foo);
object value = Activator.CreateInstance(type);
var property = type.GetProperty("Text");
property.SetValue(value, "hello", null);
Foo foo = (Foo) value;
Console.WriteLine(foo.Text);
}
}
CreateInstance will not help you with structs with no explicitly defined constructors.
FormatterServices.GetUninitializedObject(Type type);
This does the trick with blank structs.
Just to add - with immutable structs, you are likely to have to do parameter matching to the constructor. Unfortunately this is tricky when there can be multiple constructs, and especially since some types have a separate static "Create" method instead of a public constructor. But assuming you've done the matching, you can still use Activator.CreateInstance:
Type type = typeof(Padding); // just an example
object[] args = new object[] {1,2,3,4};
object obj = Activator.CreateInstance(type, args);
However - the code to pick a constructor (the above has 3...) isn't easy. You could say "pick the most complex" and then attempt to match parameter names to property names (case insensitive)...
A naïve example:
static void Main() {
Dictionary<string, object> propertyBag =
new Dictionary<string, object>();
// these are the values from your xml
propertyBag["Left"] = 1;
propertyBag["Top"] = 2;
propertyBag["Right"] = 3;
propertyBag["Bottom"] = 4;
// the type to create
Type type = typeof(Padding);
object obj = CreateObject(type, propertyBag);
}
static object CreateObject(Type type, IDictionary<string,object> propertyBag)
{
ConstructorInfo[] ctors = type.GetConstructors();
// clone the property bag and make it case insensitive
propertyBag = new Dictionary<string, object>(
propertyBag, StringComparer.OrdinalIgnoreCase);
ConstructorInfo bestCtor = null;
ParameterInfo[] bestParams = null;
for (int i = 0; i < ctors.Length; i++)
{
ParameterInfo[] ctorParams = ctors[i].GetParameters();
if (bestCtor == null || ctorParams.Length > bestParams.Length)
{
bestCtor = ctors[i];
bestParams = ctorParams;
}
}
if (bestCtor == null) throw new InvalidOperationException(
"Cannot create - no constructor");
object[] args = new object[bestParams.Length];
for (int i = 0; i < bestParams.Length; i++)
{
args[i] = propertyBag[bestParams[i].Name];
propertyBag.Remove(bestParams[i].Name);
}
object obj = bestCtor.Invoke(args);
// TODO: if we wanted, we could apply any unused keys in propertyBag
// at this point via properties
return obj;
}

Categories