Cast into new Object, Copy values by Property Name - c#

I have the following:
Class 1 (Text, State, Level)
Class 2 (Text, State, Level, Ident)
Is there a way for me to cast an object of Class 2 into into Class 1, and not having to do the usual cast code (Text = c.Text, State = c.State etc.)? Possibly by identifying the property names of each class and copying the value over?

Why not derive Class 2 from Class 1, or have a common base class?
e.g.
class Class1
{
string Text;
string State;
int Level;
}
class Class2 : Class1
{
int Ident;
// ...
}
A Class 2 instance can now be used everywhere a Class 1 instance is required.

Here is a very simple example without any error checking, it simply uses reflection to iterate over the properties of the source object and set the value of the destination object only if the types match.
class Program
{
static void Main(string[] args)
{
var bar = new Bar();
var foo = new Foo {A = 10, B = "Hello World"};
foo.CopyTo(bar);
Console.WriteLine("{0} - {1}", bar.A, bar.B);
}
}
public static class Extensions
{
public static void CopyTo(this object source, object destination)
{
var sourceType = source.GetType();
var destinationType = destination.GetType();
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
var properties = sourceType.GetProperties(flags);
foreach (var sourceProperty in properties)
{
var destinationProperty = destinationType.GetProperty(sourceProperty.Name, flags);
if (destinationProperty.PropertyType.Equals(sourceProperty.PropertyType))
{
destinationProperty.SetValue(destination, sourceProperty.GetValue(source, null), null);
}
}
}
}

Maybe the problem is more complicated than the question. If not, have you tried inheritance?
class Class1
{
//Text, State, Level
}
class Class2 : Class1
{
//Indent
}
Since Class2 inherits from class 1, you can pass it around as Class1 no casting needed. This works for example:
Class1 obj = new Class2();

Related

Cast to Type with reflection without generic argument

I have an issue which involves casting to different Types
I have a class which is a wrapper, which contains an inner wrapper which contains T here is the structure:
public class Wrapper
{
public readonly Guid Foo;
public Wrapper(Guid foo)
{
Foo = foo;
}
}
public class Wrapper<T> : Wrapper
{
public readonly InnerWrapper<T> InnerWrapper;
public Wrapper(Guid foo, InnerWrapper<T> innerWrapper)
: base(foo)
{
InnerWrapper = innerWrapper;
}
}
public class InnerWrapper<T>
{
public readonly Guid Bar;
public readonly T Content;
public InnerWrapper(T content, Guid bar)
{
Content = content;
Bar = bar;
}
}
In another part of the code I have a List<Wrapper>. notice that is a list of wrapper not a list of wrapper<T> which means that T can be any class.
when this translates is expected to have something like
{
wrapper<Class1>,
wrapper<Class2>
}
and so on.
At some point in the code there is a "helper class" which contains the object as object type and the Type
public class Helper
{
public readonly object Obj;
public readonly Type Type;
public Helper(object obj, Type type)
{
Obj = obj;
Type = type;
}
}
And here is where my problem begins, I created a helper method to convert from List<Helper> to List<Wrapper> and it does it, but the inner object is type object instead of being the actual type.
And i have no clue of how to convert from object to the actual type without using generics, and as T can be multiples types I cannot use them.
This is the helper classs
public static class HelperExtensions
{
public static T CastTo<T>(this object o) => (T)o;
public static List<Wrapper> ToWrapper(this IEnumerable<Helper> helperList)
{
List<Wrapper> wrapperResponseList = new List<Wrapper>();
foreach (var help in helperList)
{
// doesn't work either var typedValue = Convert.ChangeType(help.Obj, help.Type);
var methodInfo = typeof(HelperExtensions).GetMethod(nameof(CastTo), BindingFlags.Static | BindingFlags.Public);
var genericArguments = new[] { help.Type };
var genericMethodInfo = methodInfo?.MakeGenericMethod(genericArguments);
var typedValue = genericMethodInfo?.Invoke(null, new[] { help.Obj });
var wrapper = typedValue.BuildWrapper(Guid.NewGuid(), Guid.NewGuid());
wrapperResponseList.Add(wrapper);
}
return wrapperResponseList;
}
//This one called individually does the work properly
public static Wrapper<T> BuildWrapper<T>(this T obj, Guid foo, Guid bar)
{
InnerWrapper<T> inner = new InnerWrapper<T>(obj, bar);
return new Wrapper<T>(foo, inner);
}
}
to show the code here I also Created a test class
public class Placeholder
{
public Guid Value { get; }
public Placeholder(Guid value)
{
Value = value;
}
}
so when I call
Placeholder placeholder = new Placeholder(Guid.NewGuid());
Helper helper1 = new Helper(placeholder, placeholder.GetType());
var result = new List<Helper> { helper1 }.ToWrapper();
It generates a List<Wrapper> but instead of wrapper<T> it is generating Wrapper<Object>
Any idea how can I cast it properly?
I also created a fiddle https://dotnetfiddle.net/pz3JDz which contains all the needed code to test.
Thanks In advance for your help.
The local variable typedValue is declared with var and because genericMethodInfo?.Invoke(...) returns the reply of the type object, the type of that local variable typedValue will be object. After that you call the generic method Wrapper<T> BuildWrapper<T>(this T obj, ...) here since the type of typedValue is object the type of T is will also be object, therefore it will return Wrapper<object>.
To solve it instead of this:
var wrapper = typedValue.BuildWrapper(Guid.NewGuid(), Guid.NewGuid());
Do this:
var buildWrapperMethodInfo = typeof(HelperExtensions).GetMethod(nameof(BuildWrapper), BindingFlags.Static | BindingFlags.Public);
var buildWrapperGenericMethodInfo = buildWrapperMethodInfo?.MakeGenericMethod(genericArguments);
var wrapper = buildWrapperGenericMethodInfo?.Invoke(null, new[] { typedValue, Guid.NewGuid(), Guid.NewGuid() });
And then add the type cast here:
wrapperResponseList.Add((Wrapper)wrapper);
Simplifying the call to directly retrieve the object from the BuildWrapper<T> is possible.
You can do the following. You can obviously reformat for error check but you get the idea.
var wrapperGeneric = typeof(HelperExtensions).GetMethod("BuildWrapper").MakeGenericMethod(new[] { help.Type}).Invoke(this,help.Obj)

Invoking an Interface method using reflection

I am using reflection to invoke an Interface method and I have this error message "Object reference not set to an instance of an object." I try using ConstructorInfo too and also have error. Please help.
public class ClassA
{
private void MethodA(int num, ClassC result)
{
}
}
public interface InterfaceB
{
ClassC MethodB(int num);
}
internal class ClassB
{
public ClassC MethodB(int num)
{
}
}
Type typClassA = Type.GetType("ClassA");
Type typInterfaceB = Type.GetType("InterfaceB");
MethodInfo methodB = typInterfaceB.GetMethod("MethodB", BindingFlags.Public | BindingFlags.Instance); // Error lies here
ClassC result = (ClassC) methodB.Invoke(typInterfaceB, new object[]{num});
MethodInfo methodA = typClassA.GetMethod("MethodA", BindingFlags.NonPublic | BindingFlags.Instance);
methodA.Invoke(typClassA, new object[]{num, result});
The actual code for ClassB is not declared as "public ClassB : InterfaceB" but include more classes and ClassB is internal access. See edited codes. Apology for changing the codes so many times as I don't know how to simplify this scenario.
You have to give fully qualified name for class . please observe the below example
namespace ConsoleApplication10
{
interface IA
{
void Print();
}
class A : IA
{
public void Print()
{
Console.WriteLine("Hello");
}
}
class Program
{
static void Main(string[] args)
{
Type type = Type.GetType("ConsoleApplication10.A");
type.GetMethod("Print").Invoke(Activator.CreateInstance(type, null), null);
}
}
}
C# does not use duck-typing.
Therefore in order to actually implement an interface method, you will have to declare that your class implements that interface:
public class ClassB : InterfaceB
There are other problems in your code:
Type typInterfaceB = Type.GetType("InterfaceB"); // unless you have no namespace at all, you need to specify the fully qualified name
// Probably you got an NRE because of above issue here
MethodInfo methodB = typInterfaceB.GetMethod("MethodB", BindingFlags.Public | BindingFlags.Instance);
// this does not work, because the first argument needs to be an instance
// try "new ClassB()" instead
ClassC result = (ClassC) methodB.Invoke(typInterfaceB, new object[]{num});
i don't quite understand what you want, but i think you need something like this
public interface InterfaceB
{
int method(int d);
}
public class ClassB:InterfaceB
{
int a = 10;
public int method(int d)
{
return a + d;
}
}
var b = new ClassB();
var mi = typeof(ClassB).GetInterface("InterfaceB").GetMethod("method");
var res = mi.Invoke(b, new object[] { 10 }); // res == 20
UPDATE
yet another variant
public interface InterfaceB
{
int method(int d);
}
public class ClassB:InterfaceB
{
int a = 10;
public int method(int d)
{
return a + d;
}
}
var b = new ClassB();
var mi = typeof(InterfaceB).GetMethod("method");
var res = mi.Invoke(b, new object[] { 10 }); // res == 20
You are trying to invoke a method on an object of type System.Type, and not on the object that implements that interface.
The first parameter of MethodInfo.Invoke is the instance of the object you want to invoke the method on... you are using an instance of a System.Type object, and not one of ClassA or ClassB
All your methods are instance (not static) methods, so you have to create instances to call them, and pass these instances to Invoke, for instance:
// Let's call MethodA from ClassA
Object instanceA = new ClassA();
MethodInfo methodA = instanceA.GetType().GetMethod("MethodA", BindingFlags.NonPublic | BindingFlags.Instance);
// Pay attention to 1st argument - instanceA
methodA.Invoke(instanceA, new object[]{num, result});
...
// Since ClassB doesn't implement InterfaceB, the only option
// is to call MethodB of ClassB
Object instanceB = new ClassB();
MethodInfo methodB = instanceB.GetType().GetMethod("MethodB", BindingFlags.Public | BindingFlags.Instance);
ClassC result = (ClassC) (methodB.Invoke(instanceB, new object[]{num}));
If you are sure your object has needed method then you can use dynamic and write like this:
var result = (yourObject as dynamic).YourMethod("SomeParam");
interface IGetNames
{
string GetFirstName();
string GetMiddleName();
}
public class GetNames : IGetNames
{
public string GetFirstName()
{
return "First Name";
}
public string GetMiddleName()
{
return "Middle Name";
}
}
static void Main(string[] args)
{
Type _Interface = Type.GetType("ConsoleApplication2.IGetNames");
Type _Class = Type.GetType("ConsoleApplication2.GetNames");
InterfaceMapping GetInterfaceMap = _Class.GetInterfaceMap(_Interface);
MethodInfo methodInfo = GetInterfaceMap.TargetMethods[0];
object str = methodInfo.Invoke(Activator.CreateInstance(GetInterfaceMap.TargetType), null);
}

Why does this cloning not work?

I'm trying to clone instances of a derived class, but somehow it doesn't work well. The cloning method is:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(typeof(T));
PropertyInfo[] listOfProps = typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
FieldInfo[] listOfFields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.CreateInstance);
foreach (PropertyInfo prop in listOfProps) prop.SetValue(result, prop.GetValue(input, null), null);
foreach (FieldInfo field in listOfFields) field.SetValue(result, field.GetValue(input));
return result;
}
As you can see, I added many BindingFlags because it wasn't working. But to no avail.
It does work in a simple case:
MyclassA1 a1 = new MyclassA1();
MyclassA a = CloneFieldsAndProperties(a1);
if (a is MyclassA1) Text = "Works";
Where:
class MyclassA
{
public int i;
}
class MyclassA1 : MyclassA
{
public int i1;
}
But in my real program it doesn't. The real program's classes' declarations are long so I'm not posting them here. What might be the problem?
I had the same issue a long time ago. The only real solution for me, after lots of googling, was to serialize and deserialize it. It's not a bad solution and you lose only a little bit of performance, just do it like this:
Add this tag to your class:
[Serializable()]
public class a
{
}
And then you can create a function like this:
public object Clone()
{
IO.MemoryStream mem = new IO.MemoryStream();
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter form = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
form.Serialize(mem, this);
mem.Position = 0;
return form.Deserialize(mem);
}
If you need a shallow clone, simply use Object.MemberwiseClone. If you need a deep clone, serialize and then deserialize your object (e.g. using BinaryFormatter or DataContractSerializer). This would take care of issues such as cycles and cross references.
This will work and may be faster than the serialization method:
Code:
using System;
namespace Cloning
{
class Program
{
static void Main(string[] args)
{
Derived d = new Derived() { property = 1, field = 2, derivedProperty = 3, derivedField = 4 };
Base b = new Derived(d);
// Change things in the derived class.
d.property = 5;
d.field = 6;
d.derivedProperty = 7;
d.derivedField = 8;
// Output the copy so you know it's not shallow.
Console.WriteLine((b as Derived).property);
Console.WriteLine((b as Derived).field);
Console.WriteLine((b as Derived).derivedProperty);
Console.WriteLine((b as Derived).derivedField);
Console.ReadLine();
}
class Base
{
public int property { get; set; }
public int field;
}
class Derived : Base
{
public Derived() { }
public Derived(Derived d)
{
// Perform the deep copy here.
// Using reflection should work, but explicitly stating them definitely
// will, and it's not like it's not all known until runtime.
this.property = d.property;
this.field = d.field;
this.derivedProperty = d.derivedProperty;
this.derivedField = d.derivedField;
}
public int derivedProperty { get; set; }
public int derivedField;
}
}
}
Test:
http://goo.gl/pQnAL
Output:
1
2
3
4
Comments:
I would really imagine that this would work in more than just a trivial case but perhaps not:
public static T CloneFieldsAndProperties<T>(T input)
{
T result = (T)Activator.CreateInstance(input.GetType());
PropertyInfo[] properties = input.GetType().GetProperties();
FieldInfo[] fields = input.GetType().GetFields();
foreach (PropertyInfo property in properties)
property.SetValue(result, property.GetValue(input, null), null);
foreach (FieldInfo field in fields)
field.SetValue(result, field.GetValue(input));
return result;
}

Can an array be assigned to an array of an unknown type when you know the name of the field to assign to?

I need to assign an array to a field. I dont know the fields type, but I do have a reference to an instance and the name of the field. I can assume the array can be casted to the fields type. Can this be done?
Bas
Edit:
Hopefully this code will clarify what Im trying to do, this causes an exception in assign:
class MyClass
{
static void Main()
{
MyClass t = new MyClass();
A a = new A();
C[] c = new C[] {new B()};
t.Assign(a, "field", c);
}
void Assign(object obj, string field, object[] value)
{
// crashes
obj.GetType().GetField(field).SetValue(obj, value);
}
}
class A
{
public B[] field;
}
class B : C { }
class C { }
instance.GetType()
.GetField("fieldName", BindingFlags.Instance | BindingFlags.NonPublic)
.SetValue(instance, array);
For more information see the reflection page on MSDN.
If the code which calls this is not timecritical you can simply use the FieldInfos SetValue()
obj.GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic).SetValue(obj, newFieldValue);
If the code will be called more often you might want to dynamically compile a setter-delegate. This can be done e.g. using lightweight code generation:
Action<TObject, TField> ConstructGetter(string fieldName)
{
System.Reflection.FieldInfo field = typeof(TObject).GetField(fieldName);
DynamicMethod method = new DynamicMethod(typeof(TObject).ToString() + ":" + "Set:" + name,
null, new Type[] { typeof(TObject), typeof(TField) }, typeof(TObject));
ILGenerator generator = method.GetILGenerator();
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Stfld, field);
generator.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(Action<TObject, TField>)) as Action<TObject, TField>;
}
So a kind soul showed me the solution, enjoy : )
using System;
using System.Reflection;
class MyClass
{
static void Main()
{
MyClass t = new MyClass();
A a = new A();
C[] c = new C[] {new B()};
t.Assign(a, "field", c);
}
void Assign(object obj, string field, object[] value)
{
FieldInfo pinfo = obj.GetType().GetField(field);
Array array = Array.CreateInstance(pinfo.FieldType.GetElementType(), value.Length);
value.CopyTo(array, 0);
pinfo.SetValue(obj, array);
}
}
class A
{
public B[] field;
}
class B : C { }
class C { }

C# DataContract Serialization, how to deserialize to already existing instance

I have a class, which holds a static dictionary of all existing instances, which are defined at compile time.
Basically it looks like this:
[DataContract]
class Foo
{
private static Dictionary<long, Foo> instances = new Dictionary<long, Foo>();
[DataMember]
private long id;
public static readonly Foo A = Create(1);
public static readonly Foo B = Create(2);
public static readonly Foo C = Create(3);
private static Foo Create(long id)
{
Foo instance = new Foo();
instance.id = id;
instances.Add(instance);
return instance;
}
public static Foo Get(long id)
{
return instances[id];
}
}
There are other fields, and the class is derived, but this doesn't matter for the problem.
Only the id is serialized. When an instance of this type is deserialized, I would like to get the instance that has been created as the static field (A, B or C), using Foo.Get(id) instead of getting a new instance.
Is there a simple way to do this? I didn't find any resources which I was able to understand.
During deserialization it (AFAIK) always uses a new object (FormatterServices.GetUninitializedObject), but to get it to substitute the objects after deserialization (but before they are returned to the caller), you can implement IObjectReference, like so:
[DataContract]
class Foo : IObjectReference { // <===== implement an extra interface
object IObjectReference.GetRealObject(StreamingContext ctx) {
return Get(id);
}
...snip
}
done... proof:
static class Program {
static void Main() {
Foo foo = Foo.Get(2), clone;
DataContractSerializer ser = new DataContractSerializer(typeof(Foo));
using (MemoryStream ms = new MemoryStream()) { // clone it via DCS
ser.WriteObject(ms, foo);
ms.Position = 0;
clone = (Foo)ser.ReadObject(ms);
}
Console.WriteLine(ReferenceEquals(foo, clone)); // true
}
}
Note there are some extra notes on this for partial trust scenarios on MSDN, here.
I had a similar problem and the best solution that I found was adding some wrapper class, that was managing instances of the one needed to be serialized.
I am not sure about the exact signature with Contracts. I used SerializableAttribute, and with it i looked smth. like that:
[Serializable]
class FooSerializableWrapper : ISerializable
{
private readonly long id;
public Foo Foo
{
get
{
return Foo.Get(id);
}
}
public FooSerializableWrapper(Foo foo)
{
id = foo.id;
}
protected FooSerializableWrapper(SerializationInfo info, StreamingContext context)
{
id = info.GetInt64("id");
}
void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("id", id);
}
}
You may be able to get a step towards what you are looking for using OnDeserializingAttribute. However, that will probably only let you set properties (so you could have what amounts to a Copy method that populates all the properties of the current instance using your static instance.
I think if you actually want to return your static instances, you'd probably have to write your own Deserializer...
Untested, but I would assume you could implement a deserializer pretty easily like this:
public class MyDeserializer : System.Xml.Serialization.XmlSerializer
{
protected override object Deserialize(System.Xml.Serialization.XmlSerializationReader reader)
{
Foo obj = (Foo)base.Deserialize(reader);
return Foo.Get(obj.id);
}
}
Note that you'll have to do something about getting the ID since it is private in your code; Also this assumes you are using XML serialization; Replace the inheritance with whatever you actually are using. And finally, this means you'll have to instantiate this type when deserializing your objects, which may involve changing some code and/or configuration.
No problem, just use 2 classes. In getObject method you get your existing object
[Serializable]
public class McRealObjectHelper : IObjectReference, ISerializable
{
Object m_realObject;
virtual object getObject(McObjectId id)
{
return id.GetObject();
}
public McRealObjectHelper(SerializationInfo info, StreamingContext context)
{
McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
m_realObject = getObject(id);
if(m_realObject == null)
return;
Type t = m_realObject.GetType();
MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
List<object> data = new List<object>(members.Length);
foreach(MemberInfo mi in members)
{
Type dataType = null;
if(mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dataType = fi.FieldType;
} else if(mi.MemberType == MemberTypes.Property){
PropertyInfo pi = mi as PropertyInfo;
dataType = pi.PropertyType;
}
try
{
if(dataType != null){
data.Add(info.GetValue(mi.Name, dataType));
deserializeMembers.Add(mi);
}
}
catch (SerializationException)
{
//some fiels are missing, new version, skip this fields
}
}
FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
}
public object GetRealObject( StreamingContext context )
{
return m_realObject;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
public class McRealObjectBinder: SerializationBinder
{
String assemVer;
String typeVer;
public McRealObjectBinder(String asmName, String typeName)
{
assemVer = asmName;
typeVer = typeName;
}
public override Type BindToType( String assemblyName, String typeName )
{
Type typeToDeserialize = null;
if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
{
return typeof(McRealObjectHelper);
}
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
}
Then, when deserialize:
BinaryFormatter bf = new BinaryFormatter(null, context);
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
bf.Deserialize(memStream);

Categories

Resources