I have class method:
public object MyMethod(object obj)
{
// I want to add some new properties example "AddedProperty = true"
// What must be here?
// ...
return extendedObject;
}
And:
var extendedObject = this.MyMethod( new {
FirstProperty = "abcd",
SecondProperty = 100
});
Now the extendedObject has new properties. Help please.
You can't do that.
If you want a dynamic type that you can add members to at runtime then you could use an ExpandoObject.
Represents an object whose members can be dynamically added and removed at run time.
This requires .NET 4.0 or newer.
You can use a Dictionary (property, value) or if you are using c# 4.0 you can use the new dynamic object (ExpandoObject).
http://msdn.microsoft.com/en-us/library/dd264736.aspx
Do you know at compile-time the names of the properties? Because you can do this:
public static T CastByExample<T>(object o, T example) {
return (T)o;
}
public static object MyMethod(object obj) {
var example = new { FirstProperty = "abcd", SecondProperty = 100 };
var casted = CastByExample(obj, example);
return new {
FirstProperty = casted.FirstProperty,
SecondProperty = casted.SecondProperty,
AddedProperty = true
};
}
Then:
var extendedObject = MyMethod(
new {
FirstProperty = "abcd",
SecondProperty = 100
}
);
var casted = CastByExample(
extendedObject,
new {
FirstProperty = "abcd",
SecondProperty = 100,
AddedProperty = true
}
);
Console.WriteLine(xyz.AddedProperty);
Note that this relies very heavily on the fact that two anonymous types in the same assembly with properties having the same name of the same type in the same order are of the same type.
But, if you're going to do this, why not just make concrete types?
Output:
True
Related
I am creating a Unity custom window, This window displays data from from an object that will eventually be converted to JSON.
I am able to read the data and modify it as long as it isn't an array which is where I am having issues.
The data looks like this:
public static class GameData {
private static SaveData data;
public static SaveData save { get { return data.save; } }
[System.Serializable]
public class SaveData {
public int energyCurrent = 100;
public float speed = 2.5f;
public List<int> itm = new List<int>() { 1, 2, 3, 4, 5, 6 };
}
}
I then have an object which stores each item like this (where they key is the field name and value is the field value; ex: key=speed, value=2.5f):
class KeyValue {
public string key;
public object value;
public KeyValue(string key, object value) {
this.key = key;
this.value = value;
}
}
It is then stored within a list:
List<KeyValue> keyValues = new List<KeyValue>();
The part I am having issues with is the save which looks like this:
void SaveDataLocal() {
keyValues.ForEach(item => {
// GameData.save is a reference to SaveData
var field = GameData.save.GetType().GetField(item.key);
field.SetValue(GameData.save, GetValue(item));
});
GameData.Save();
}
object GetValue(KeyValue keyVal) {
var value = keyVal.value;
if (value.GetType().IsArray || isList(value)) {
List<object> list = new List<object>();
((List<KeyValue>)value).ForEach(item => {
list.add(GetValue(item));
});
return list;
} else {
return value;
}
}
I have tried two ways to update the value, however, I am getting an error saying:
ArgumentException: Object type System.Collections.Generic.List`1[System.Object] cannot be converted to target type: System.Collections.Generic.List`1[System.Int32]
I am trying to use Reflection because list type could be int, float, Vector3, etc. so I need it to by dynamic, and I cannot use a dynamic type because we are not using 4.x.
The error is taking place here:
field.SetValue(GameData.save, GetValue(item));
GetValue() returning a List<object> when the field is a List<int>. What can I do to pass the List<object>?
Edit
I have tried casting it like so:
public static T Cast<T>(object o) {
return (T)o;
}
keyValues.ForEach(item => {
var field = GameData.save.GetType().GetField(item.key);
var val = GetValue(item);
var castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(field.FieldType);
var r = castMethod.Invoke(null, new object[] { val });
field.SetValue(GameData.save, r);
});
But I am getting this error:
InvalidCastException: Cannot cast from source type to destination type.
GameSmart.SaveData.Cast[List`1]
I am not sure if this is the cleanest way, but I basically had to do a cast by looping over all the items in the list/array and individually casting them. I can't seem to cast an entire list at once.
public static List<T> CastList<T>(List<object> o) {
var list = new List<T>();
foreach (var i in o) { list.Add((T)i); }
return list;
}
void SaveDataLocal() {
keyValues.ForEach(item => {
var field = GameData.save.GetType().GetField(item.key);
var value = GetValue(item);
if (value.GetType().IsArray || isList(value)) {
Type type;
if (isList(value)) type = field.FieldType.GetGenericArguments().Single();
else type = field.FieldType.GetElementType();
var castMethod = this.GetType().GetMethod("CastList").MakeGenericMethod(type);
value = castMethod.Invoke(null, new object[] { value });
}
field.SetValue(GameData.save, value);
});
GameData.Save();
}
I want to initialize an expandoObject from List.
internal class CarKeyValue {
public CarKey CarKey { get; set; }
public string Value1 { get; set; }
public string Value2 { get; set; }
}
public enum CarKey {
Brand = 1,
Model = 2,
Year = 3,
FactoryLocation = 4,
//more than 400 key here...
}
var car = new List<CarKeyValue>{
new CarKeyValue {CarKey = CarKey.Brand, Value1 = "Ford"},
new CarKeyValue {CarKey = CarKey.Model, Value1 = "Focus",Value2 = "Titanium"},
new CarKeyValue {CarKey = CarKey.Year, Value1 = "1995"},
new CarKeyValue {CarKey = CarKey.FactoryLocation, Value1 = "Turkey",Value2="Bursa"},
};
dynamic expando = new ExpandoObject();
foreach(var item in car){
expando.[item.CarKey].Value1 = item.Value1;
//Incorrect primary expression.
expando.[item.CarKey].Value2 = item.Value2;
}
How I do that? I need to use Expando Object. I try to use IDictionary<string,dynamic> but that thrown another exception.
Is there any possible way?
Yes, you can, but it makes no sense. You have to cast your expando object to IDictionary<string, object> or IDictionary<string, dynamic>.
IDictionary<string, object> expando = new ExpandoObject();
foreach (var item in car)
{
expando[item.CarKey.ToString()].Value1 = item.Value1;
}
The above code fails because you never assign a value to expando[item.CarKey.ToString()]. Instead you want to set a property from a non-existing object. Regular code would have thrown a NullReferenceException here. (You can read the above code as expando.SomeProperty.Value1, where SomeProperty is null.)
So once you have set the object to an instance of something, you can use it, but then there is not much use in using the ExpandoObject any more:
Foo foo = new Foo();
expando[item.CarKey.ToString()] = foo;
foo.Value1 = item.Value1;
My solution is here. I use IDictionary. Not neccessary using Foo() or something else. This code block working well.
IDictionary<string,dynamic> expando = new ExpandoObject();
foreach(var item in car) {
expando.Add(item.CarKey.ToString(),null);
expando[item.CarKey.ToString()] = new { Value1 = item.Value1,Value2 = item.Value2 };
}
I want to achieve as per below
// Property
public static T Type
{ get; set; }
private void Form1_Load(...)
{
List<Type> abc = new ...
// code.....
As mentioned above, when I load Form1, a List must be defined of the type = Type (the property). How can I achieve this?
Type is the name of the property, T is the type name.
List<T> abc = new List<T>();
I'm assuming of course that you've parameterized the enclosing class as class CustomForm<T>
Not sure why you would want to set that using a property, or if that is at all possible - Do you want to be able to change the type after instantiation or something?
If you don't have some very special needs, perhaps you could just use this?:
private void Form1_Load<T>(...) // Pass the type in here
{
List<T> abc = new List<T>();
}
Usage:
this.Form1_Load<targetType>(...);
Alternatively, pass the type in when you instantiate the class containing Form1_Load():
class Container<T>
{
private void Form1_Load(...)
{
List<T> abc = new List<T>();
}
}
Usage:
var instance = new Container<targetType>();
You could create a class builder like this:
public class GenericBuilder
{
public Type ParamType { get; set; }
public object CreateList() {
var listType = typeof(List<>);
ArrayList alist = new ArrayList();
alist.Add( this.ParamType );
var targetType = listType.MakeGenericType( (Type[])alist.ToArray(typeof(Type)) );
return Activator.CreateInstance( targetType );
}
}
and then you could use it:
var builder = new GenericBuilder();
builder.ParamType = typeof( int );
var result = builder.CreateList();
I have the following class:
public class foo
{
public Dictionary<string, string> data = new Dictionary<string, string>();
public foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
}
I need get the value of dictionary using the dot operadotor it's because the class that I set the class as arguments use the dynamic keyword to "walk" on the class.
for example:
var args = new[] {"a","b","c"};
var Foo = new foo(args);
var baa = Foo.data.a;
Console.Write(baa); // ..
if exists an way to make dynamic variables, something like:
public foo(params object[] args) {
foreach (object arg in args) {
var name = (string) arg;
var value = "..";
MakeVariable(name, value);
}
}
makes an variable named of arg and the value .. as public member of foo class.
anyway differents to solve this is very appreciated. Thanks in advance.
You can have Foo inherit from DynamicObject:
public class Foo : DynamicObject
{
private Dictionary<string, string> data = new Dictionary<string, string>();
public Foo(params object[] args)
{
foreach (object arg in args)
{
data.Add(arg.ToString(), "..");
}
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (data.ContainsKey(binder.Name))
{
result = data[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
}
To use it you can use dynamic to hold an instance of Foo:
var args= new[] { "a", "b", "c" };
dynamic foo = new Foo(args);
var myA = foo.a; //returns ".."
Keep in mind that you will lose type safety since you have to use dynamic - your use case should really justify this disadvantage - usually there is a better approach.
I think you should use DynamicObject. If you are using an older version of the framework the only option is Reflection.Emit
The dynamic works something like this
// If you try to get a value of a property
// not defined in the class, this method is called.
public override bool TryGetMember(
GetMemberBinder binder, out object result)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
string name = binder.Name.ToLower();
// If the property name is found in a dictionary,
// set the result parameter to the property value and return true.
// Otherwise, return false.
return dictionary.TryGetValue(name, out result);
}
// If you try to set a value of a property that is
// not defined in the class, this method is called.
public override bool TrySetMember(
SetMemberBinder binder, object value)
{
// Converting the property name to lowercase
// so that property names become case-insensitive.
dictionary[binder.Name.ToLower()] = value;
// You can always add a value to a dictionary,
// so this method always returns true.
return true;
}
Another option is to use the ExpandoObject class if you want to directly expose the Data member, as in your example. This keeps the code simpler if you don't need to define specific operations that would require inheriting DynamicObject.
public class Foo
{
public dynamic Data = new ExpandoObject();
public Foo(params object[] args)
{
var dataDict = (IDictionary<string, object>)Data;
foreach (var obj in args)
{
dataDict.Add(obj.ToString(), "..");
}
}
}
Usage:
var foo = new Foo("a", "b", "c");
Console.WriteLine(foo.Data.a);
((IDictionary<string, object>)foo.Data).Add("d", "!");
foreach (var item in foo.Data)
{
Console.WriteLine("{0} : {1}", item.Key, item.Value);
}
Notice that I cast to a dictionary and added "d" although I could've also assigned it directly: foo.Data.d = "!". The only difference is you may not know ahead of time what field names you have, and the former example allows you to setup the ExpandoObject based on dynamic input, whereas the latter is useful when you already know what field name to use.
In .NET 4 this exact behavior is implemented by ExpandoObject class:
public class Foo
{
private readonly ExpandoObject _dict = new ExpandoObject();
public dynamic Data
{
get { return _dict; }
}
public Foo(params object[] args)
{
foreach (var arg in args)
_dict.Add(arg.ToString(), "..");
}
}
var foo = new Foo("a", "b", "c");
foo.Data.x = 3.14;
Console.Write(foo.Data.a);
How can I set generic type dynamically?
public class A
{
public int X { get; set; }
public A()
{
X = 9000;
}
}
public class Class1
{
public void Test()
{
List<A> theList = new List<A>() {
new A { X = 1 },
new A { X = 2 }
};
object testObj = theList;
var argType = testObj.GetType().GetGenericArguments()[0];
Foo(testObj as ICollection<argType>); // ?
}
public void Foo<T>(ICollection<T> items) where T:new()
{
T newItem = new T();
items.Add(newItem);
}
To do in "regular" c# you would use reflection to obtain the MethodInfo, then use MakeGenericMethod() and Invoke(). However, this is easier:
Foo((dynamic)testObj);
The reflection approach here is:
var method = typeof(Class1).GetMethod("Foo").MakeGenericMethod(argType);
method.Invoke(this, new object[] { testObj });
You can't do that, because in the Foo function you are supposed to do something with the collection, and there's no guarantee that the type will be safe.
The only way is using an "object" then casting to the proper type within the Fooo function.