Using Reflection to copy across equivalent properties between struct and class - c#

I'm making a game (see http://haighshroom.blogspot.com for context) and saving/loading the game using an XML file. It currently uses a different struct for the save format of each class, but continuing to update the save file structure is proving tiresome so I want to try and adopt the following generalised method.
Let's assume I have 3 classes, which all inherit from BaseClass:
Class1, which has Property1, Property2 (let's say all properties are ints)
Class2, which has Property2, Property3
Class3, which has Property1, Property3
Now my new generalised SaveStruct would look like this:
public struct EntityStruct
{
public string ClassName;
public int Property1;
public int Property2;
public int Property3;
public EntityStruct()
{
ClassName = "";
Property1 = Property2 = Property3 = 0;
}
}
When saving/loading a particular entity, I want to achieve the following Pseudocode (both functions called from BaseClass):
public EntityStruct GetSaveStruct()
{
EntityStruct es = new EntityStruct();
es.ClassName = this.GetType().Name;
if Exists(this.Property1) es.Property1 = Get(this.Property1);
if Exists(this.Property2) es.Property2 = Get(this.Property2);
if Exists(this.Property3) es.Property3 = Get(this.Property3);
}
public void LoadFromStruct(EntityStruct es)
{
BaseClass e = (BaseClass)(Activator.CreateInstance(null, GV.NameSpace + es.ClassName).Unwrap());
if Exists(e.Property1) Set(e.Property1 = es.Property1);
if Exists(e.Property2) Set(e.Property2 = es.Property2);
if Exists(e.Property3) Set(e.Property3 = es.Property3);
}
The parts I don't know how to define are:
-Exists(e.Property1) - this needs to use Reflection to determine whether the instance e has Property1 defined (we are calling this from BaseClass so we don't know without using Reflection).
-Get(e.Property1) - if Property 1 does exist for an instance e, we need to get its value
-Set(e.Property1 = es.Property1) - if Property 1 does exist from an instance e, we need to set its value
Many thanks.

you can try use this code
public void ShallowCopyValues<T1, T2>(T1 firstObject, T2 secondObject)
{
const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
var firstFieldDefinitions = firstObject.GetType().GetFields(bindingFlags);
IEnumerable<FieldInfo> secondFieldDefinitions = secondObject.GetType().GetFields(bindingFlags);
foreach (var fieldDefinition in firstFieldDefinitions)
{
var matchingFieldDefinition = secondFieldDefinitions.FirstOrDefault(fd => fd.Name == fieldDefinition.Name &&
fd.FieldType == fieldDefinition.FieldType);
if (matchingFieldDefinition == null)
continue;
var value = fieldDefinition.GetValue(firstObject);
matchingFieldDefinition.SetValue(secondObject, value);
}
}

The starting point for all this is the System.Type class. You can get an instance of this for your type using e.GetType().
To look for a field, use GetField. If that returns null, then the field doesn't exist at all.
If it returns a value (of type FieldInfo) then use GetValue to get the value and SetValue to set it.
Reflection is relatively slow, so if performance is a concern, grab the System.Type object ahead of time with something like System.Type.getType(name) and also get the FieldInfo objects. You don't need the actual instance of the class to do either of those two operations, though obviously you need it to get and set the field values.

Related

Why is it impossible to cast to a derived type [duplicate]

Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?.
I have tried it and it creates a run-time error.
No. A reference to a derived class must actually refer to an instance of the derived class (or null). Otherwise how would you expect it to behave?
For example:
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?
If you want to be able to convert an instance of the base type to the derived type, I suggest you write a method to create an appropriate derived type instance. Or look at your inheritance tree again and try to redesign so that you don't need to do this in the first place.
No, that's not possible since assigning it to a derived class reference would be like saying "Base class is a fully capable substitute for derived class, it can do everything the derived class can do", which is not true since derived classes in general offer more functionality than their base class (at least, that's the idea behind inheritance).
You could write a constructor in the derived class taking a base class object as parameter, copying the values.
Something like this:
public class Base {
public int Data;
public void DoStuff() {
// Do stuff with data
}
}
public class Derived : Base {
public int OtherData;
public Derived(Base b) {
this.Data = b.Data;
OtherData = 0; // default value
}
public void DoOtherStuff() {
// Do some other stuff
}
}
In that case you would copy the base object and get a fully functional derived class object with default values for derived members. This way you can also avoid the problem pointed out by Jon Skeet:
Base b = new Base();//base class
Derived d = new Derived();//derived class
b.DoStuff(); // OK
d.DoStuff(); // Also OK
b.DoOtherStuff(); // Won't work!
d.DoOtherStuff(); // OK
d = new Derived(b); // Copy construct a Derived with values of b
d.DoOtherStuff(); // Now works!
Solution with JsonConvert (instead of typecast)
Today i faced the same issue and i found a simple and quick solution to the problem using JsonConvert.
var base = new BaseClass();
var json = JsonConvert.SerializeObject(base);
DerivedClass derived = JsonConvert.DeserializeObject<DerivedClass>(json);
I had this problem and solved it by adding a method that takes a type parameter and converts the current object into that type.
public TA As<TA>() where TA : Base
{
var type = typeof (TA);
var instance = Activator.CreateInstance(type);
PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
property.SetValue(instance, property.GetValue(this, null), null);
}
return (TA)instance;
}
That means that you can use it in you code like this:
var base = new Base();
base.Data = 1;
var derived = base.As<Derived>();
Console.Write(derived.Data); // Would output 1
As many others have answered, No.
I use the following code on those unfortunate occasions when I need to use a base type as a derived type. Yes it is a violation of the Liskov Substitution Principle (LSP) and yes most of the time we favor composition over inheritance. Props to Markus Knappen Johansson whose original answer this is based upon.
This code in the base class:
public T As<T>()
{
var type = typeof(T);
var instance = Activator.CreateInstance(type);
if (type.BaseType != null)
{
var properties = type.BaseType.GetProperties();
foreach (var property in properties)
if (property.CanWrite)
property.SetValue(instance, property.GetValue(this, null), null);
}
return (T) instance;
}
Allows:
derivedObject = baseObect.As<derivedType>()
Since it uses reflection, it is "expensive". Use accordingly.
No it is not possible, hence your runtime error.
But you can assign an instance of a derived class to a variable of base class type.
As everyone here said, that's not possible directly.
The method I prefer and is rather clean, is to use an Object Mapper like AutoMapper.
It will do the task of copying properties from one instance to another (Not necessarily the same type) automatically.
In c# 9.0 you can try to use records for this. They have default copy constructor that copy all fields - no need to use reflection / constructor with all fields.
public record BaseR
{
public string Prop1 { get; set; }
}
public record DerivedR : BaseR
{
public DerivedR(BaseR baseR) : base(baseR) { }
public string Prop2 { get; set; }
}
var baseR = new BaseR { Prop1 = "base prob" };
var derivedR = new DerivedR(baseR) { Prop2 = "new prop" };
Not in the Traditional Sense... Convert to Json, then to your object, and boom, done! Jesse above had the answer posted first, but didn't use these extension methods which make the process so much easier. Create a couple of extension methods:
public static string ConvertToJson<T>(this T obj)
{
return JsonConvert.SerializeObject(obj);
}
public static T ConvertToObject<T>(this string json)
{
if (string.IsNullOrEmpty(json))
{
return Activator.CreateInstance<T>();
}
return JsonConvert.DeserializeObject<T>(json);
}
Put them in your toolbox forever, then you can always do this:
var derivedClass = baseClass.ConvertToJson().ConvertToObject<derivedClass>();
Ah, the power of JSON.
There are a couple of gotchas with this approach: We really are creating a new object, not casting, which may or may not matter. Private fields will not be transferred, constructors with parameters won't be called, etc. It is possible that some child json won't be assigned. Streams are not innately handled by JsonConvert. However, if our class doesn't rely on private fields and constructors, this is a very effective method of moving data from class to class without mapping and calling constructors, which is the main reason why we want to cast in the first place.
Expanding on #ybo's answer - it isn't possible because the instance you have of the base class isn't actually an instance of the derived class. It only knows about the members of the base class, and doesn't know anything about those of the derived class.
The reason that you can cast an instance of the derived class to an instance of the base class is because the derived class actually already is an instance of the base class, since it has those members already. The opposite cannot be said.
You can cast a variable that is typed as the base-class to the type of a derived class; however, by necessity this will do a runtime check, to see if the actual object involved is of the correct type.
Once created, the type of an object cannot be changed (not least, it might not be the same size). You can, however, convert an instance, creating a new instance of the second type - but you need to write the conversion code manually.
You have to use an object cloner/copier that will assign all the properties one by one.
Doing this by hand is inefficient and not future-proof. But serializing & deserializing to JSON and back is not the best solution, it is slow and very memory inefficient.
However, using AutoMapper is fast. PropMapper is even faster.
PS. Disclosure: I am a contributor at PropMapper open source project.
No, it is not possible.
Consider a scenario where an ACBus is a derived class of base class Bus. ACBus has features like TurnOnAC and TurnOffAC which operate on a field named ACState. TurnOnAC sets ACState to on and TurnOffAC sets ACState to off. If you try to use TurnOnAC and TurnOffAC features on Bus, it makes no sense.
class Program
{
static void Main(string[] args)
{
a a1 = new b();
a1.print();
}
}
class a
{
public a()
{
Console.WriteLine("base class object initiated");
}
public void print()
{
Console.WriteLine("base");
}
}
class b:a
{
public b()
{
Console.WriteLine("child class object");
}
public void print1()
{
Console.WriteLine("derived");
}
}
}
when we create a child class object,the base class object is auto initiated so base class reference variable can point to child class object.
but not vice versa because a child class reference variable can not point to base class object because no child class object is created.
and also notice that base class reference variable can only call base class member.
There actually IS a way to do this. Think about how you might use Newtonsoft JSON to deserialize an object from json. It will (or at least can) ignore missing elements and populate all the elements that it does know about.
So here's how I did it. A small code sample will follow my explanation.
Create an instance of your object from the base class and populate it accordingly.
Using the "jsonconvert" class of Newtonsoft json, serialize that object into a json string.
Now create your sub class object by deserializing with the json string created in step 2. This will create an instance of your sub class with all the properties of the base class.
This works like a charm! So.. when is this useful? Some people asked when this would make sense and suggested changing the OP's schema to accommodate the fact that you can't natively do this with class inheritance (in .Net).
In my case, I have a settings class that contains all the "base" settings for a service. Specific services have more options and those come from a different DB table, so those classes inherit the base class. They all have a different set of options. So when retrieving the data for a service, it's much easier to FIRST populate the values using an instance of the base object. One method to do this with a single DB query. Right after that, I create the sub class object using the method outlined above. I then make a second query and populate all the dynamic values on the sub class object.
The final output is a derived class with all the options set. Repeating this for additional new sub classes takes just a few lines of code. It's simple, and it uses a very tried and tested package (Newtonsoft) to make the magic work.
This example code is vb.Net, but you can easily convert to c#.
' First, create the base settings object.
Dim basePMSettngs As gtmaPayMethodSettings = gtmaPayments.getBasePayMethodSetting(payTypeId, account_id)
Dim basePMSettingsJson As String = JsonConvert.SerializeObject(basePMSettngs, Formatting.Indented)
' Create a pmSettings object of this specific type of payment and inherit from the base class object
Dim pmSettings As gtmaPayMethodAimACHSettings = JsonConvert.DeserializeObject(Of gtmaPayMethodAimACHSettings)(basePMSettingsJson)
You can use an Extention:
public static void CopyOnlyEqualProperties<T>(this T objDest, object objSource) where T : class
{
foreach (PropertyInfo propInfo in typeof(T).GetProperties())
if (objSource.GetType().GetProperties().Any(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()))
propInfo.SetValue(objDest, objSource.GetType().GetProperties().First(z => z.Name == propInfo.Name && z.GetType() == propInfo.GetType()).GetValue(objSource));
}
In Code:
public class BaseClass
{
public string test{ get; set;}
}
public Derived : BaseClass
{
//Some properies
}
public void CopyProps()
{
BaseClass baseCl =new BaseClass();
baseCl.test="Hello";
Derived drv=new Derived();
drv.CopyOnlyEqualProperties(baseCl);
//Should return Hello to the console now in derived class.
Console.WriteLine(drv.test);
}
Might not be relevent, but I was able to run code on a derived object given its base. It's definitely more hacky than I'd like, but it works:
public static T Cast<T>(object obj)
{
return (T)obj;
}
...
//Invoke parent object's json function
MethodInfo castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(baseObj.GetType());
object castedObject = castMethod.Invoke(null, new object[] { baseObj });
MethodInfo jsonMethod = baseObj.GetType ().GetMethod ("ToJSON");
return (string)jsonMethod.Invoke (castedObject,null);
You can do this using generic.
public class BaseClass
{
public int A { get; set; }
public int B { get; set; }
private T ConvertTo<T>() where T : BaseClass, new()
{
return new T
{
A = A,
B = B
}
}
public DerivedClass1 ConvertToDerivedClass1()
{
return ConvertTo<DerivedClass1>();
}
public DerivedClass2 ConvertToDerivedClass2()
{
return ConvertTo<DerivedClass2>();
}
}
public class DerivedClass1 : BaseClass
{
public int C { get; set; }
}
public class DerivedClass2 : BaseClass
{
public int D { get; set; }
}
You get three benefits using this approach.
You are not duplicating the code
You are not using reflection (which is slow)
All of your conversions are in one place
I know this is old but I've used this successfully for quite a while.
private void PopulateDerivedFromBase<TB,TD>(TB baseclass,TD derivedclass)
{
//get our baseclass properties
var bprops = baseclass.GetType().GetProperties();
foreach (var bprop in bprops)
{
//get the corresponding property in the derived class
var dprop = derivedclass.GetType().GetProperty(bprop.Name);
//if the derived property exists and it's writable, set the value
if (dprop != null && dprop.CanWrite)
dprop.SetValue(derivedclass,bprop.GetValue(baseclass, null),null);
}
}
I combined some portions of the previous answers (thanks to those authors) and put together a simple static class with two methods that we're using.
Yes, it's simple, no it doesn't cover all scenarios, yes it could be expanded and made better, no it's not perfect, yes it could possibly be made more efficient, no it's not the greatest thing since sliced bread, yes there are full-on robust nuget package object mappers out there that are way better for heavy use, etc etc, yada yada - but it works for our basic needs though :)
And of course it will try to map values from any object to any object, derived or not (only the public properties that are named the same of course - ignores the rest).
USAGE:
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
// creates new object of type "RealPerson" and assigns any matching property
// values from the puppet object
// (this method requires that "RealPerson" have a parameterless constructor )
RealPerson person = ObjectMapper.MapToNewObject<RealPerson>(puppet);
// OR
// create the person object on our own
// (so RealPerson can have any constructor type that it wants)
SesameStreetCharacter puppet = new SesameStreetCharacter() { Name = "Elmo", Age = 5 };
RealPerson person = new RealPerson("tall") {Name = "Steve"};
// maps and overwrites any matching property values from
// the puppet object to the person object so now our person's age will get set to 5 and
// the name "Steve" will get overwritten with "Elmo" in this example
ObjectMapper.MapToExistingObject(puppet, person);
STATIC UTILITY CLASS:
public static class ObjectMapper
{
// the target object is created on the fly and the target type
// must have a parameterless constructor (either compiler-generated or explicit)
public static Ttarget MapToNewObject<Ttarget>(object sourceobject) where Ttarget : new()
{
// create an instance of the target class
Ttarget targetobject = (Ttarget)Activator.CreateInstance(typeof(Ttarget));
// map the source properties to the target object
MapToExistingObject(sourceobject, targetobject);
return targetobject;
}
// the target object is created beforehand and passed in
public static void MapToExistingObject(object sourceobject, object targetobject)
{
// get the list of properties available in source class
var sourceproperties = sourceobject.GetType().GetProperties().ToList();
// loop through source object properties
sourceproperties.ForEach(sourceproperty => {
var targetProp = targetobject.GetType().GetProperty(sourceproperty.Name);
// check whether that property is present in target class and is writeable
if (targetProp != null && targetProp.CanWrite)
{
// if present get the value and map it
var value = sourceobject.GetType().GetProperty(sourceproperty.Name).GetValue(sourceobject, null);
targetobject.GetType().GetProperty(sourceproperty.Name).SetValue(targetobject, value, null);
}
});
}
}
You can use a copy constructor that immediately invokes the instance constructor, or if your instance constructor does more than assignments have the copy constructor assign the incoming values to the instance.
class Person
{
// Copy constructor
public Person(Person previousPerson)
{
Name = previousPerson.Name;
Age = previousPerson.Age;
}
// Copy constructor calls the instance constructor.
public Person(Person previousPerson)
: this(previousPerson.Name, previousPerson.Age)
{
}
// Instance constructor.
public Person(string name, int age)
{
Name = name;
Age = age;
}
public int Age { get; set; }
public string Name { get; set; }
}
Referenced the Microsoft C# Documentation under Constructor for this example having had this issue in the past.
With regarding #MarkusKnappenJohansson answer and below comments we can change his code extending extension function :) so it may update an existing deriving class instance via this code :
public static TDerived As<TDerived>(this Base baseInstance, TDerived updateDerivedInstance = null) where TDerived : Base, new()
{
Type baseType = typeof(Base);
Type derivedType = typeof(TDerived);
PropertyInfo[] properties = baseType.GetProperties();
object instanceDerived = null;
if (updateDerivedInstance == null)
{
instanceDerived = Activator.CreateInstance(derivedType);
}
else
{
instanceDerived = (object)(updateDerivedInstance);
}
foreach (PropertyInfo property in properties)
{
if (property.CanWrite)
{
property.SetValue(instanceDerived, property.GetValue(baseInstance, null), null);
}
}
return (TDerived)instanceDerived;
}
Usage for getting new derived Instance is var base = new Base(); base.Data = 1; var derived = base.As<Derived>(); Console.Write(derived.Data); // Would output 1
Usage for updating existing derived Instance is var derived = new Derived(); var base = new Base(); base.Data = 1; var derivedUpdated = base.As<Derived>(derived); Console.Write(derivedUpdated.Data); // Would output 1
Another solution is to add extension method like so:
public static void CopyProperties(this object destinationObject, object sourceObject, bool overwriteAll = true)
{
try
{
if (sourceObject != null)
{
PropertyInfo[] sourceProps = sourceObject.GetType().GetProperties();
List<string> sourcePropNames = sourceProps.Select(p => p.Name).ToList();
foreach (PropertyInfo pi in destinationObject.GetType().GetProperties())
{
if (sourcePropNames.Contains(pi.Name))
{
PropertyInfo sourceProp = sourceProps.First(srcProp => srcProp.Name == pi.Name);
if (sourceProp.PropertyType == pi.PropertyType)
if (overwriteAll || pi.GetValue(destinationObject, null) == null)
{
pi.SetValue(destinationObject, sourceProp.GetValue(sourceObject, null), null);
}
}
}
}
}
catch (ApplicationException ex)
{
throw;
}
}
then have a constructor in each derived class that accepts base class:
public class DerivedClass: BaseClass
{
public DerivedClass(BaseClass baseModel)
{
this.CopyProperties(baseModel);
}
}
It will also optionally overwrite destination properties if already set (not null) or not.
Is it possible to assign a base class object to a derived class reference with an explicit typecast in C#?.
Not only explicit, but also implicit conversions are possible.
C# language doesn't permit such conversion operators, but you can still write them using pure C# and they work. Note that the class which defines the implicit conversion operator (Derived) and the class which uses the operator (Program) must be defined in separate assemblies (e.g. the Derived class is in a library.dll which is referenced by program.exe containing the Program class).
//In library.dll:
public class Base { }
public class Derived {
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) {
return new Derived(a); //Write some Base -> Derived conversion code here
}
[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) {
return new Derived(a); //Write some Base -> Derived conversion code here
}
}
//In program.exe:
class Program {
static void Main(string[] args) {
Derived z = new Base(); //Visual Studio can show squiggles here, but it compiles just fine.
}
}
When you reference the library using the Project Reference in Visual Studio, VS shows squiggles when you use the implicit conversion, but it compiles just fine. If you just reference the library.dll, there are no squiggles.
How about:
public static T As<T>(this object obj)
{
return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject(obj));
}
Best way to add all base properties to derived item is use reflection in costructor. Try this code, without creating methods or instances.
public Derived(Base item) :base()
{
Type type = item.GetType();
System.Reflection.PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties)
{
try
{
property.SetValue(this, property.GetValue(item, null), null);
}
catch (Exception) { }
}
}
I disagree that it is not possible. You can do it like this:
public class Auto
{
public string Make {get; set;}
public string Model {get; set;}
}
public class Sedan : Auto
{
public int NumberOfDoors {get; set;}
}
public static T ConvertAuto<T>(Sedan sedan) where T : class
{
object auto = sedan;
return (T)loc;
}
Usage:
var sedan = new Sedan();
sedan.NumberOfDoors = 4;
var auto = ConvertAuto<Auto>(sedan);
This is how I solved this for fields. You can do the same iteration through properties if you want. You may want to do some checks for null etc. but this is the idea.
public static DerivedClass ConvertFromBaseToDerived<BaseClass, DerivedClass>(BaseClass baseClass)
where BaseClass : class, new()
where DerivedClass : class, BaseClass, new()
{
DerivedClass derived = (DerivedClass)Activator.CreateInstance(typeof(DerivedClass));
derived.GetType().GetFields().ToList().ForEach(field =>
{
var base_ = baseClass.GetType().GetField(field.Name).GetValue(baseClass);
field.SetValue(derived, base_);
});
return derived;
}
You can just serialize the base object to JSON and then deserialize it to the derived object.
No, see this question which I asked - Upcasting in .NET using generics
The best way is to make a default constructor on the class, construct and then call an Initialise method

Perform a 'task' via an enum and a field, by checking the 'T' type, and inferring it into the task automatically

I've come across an issue that I cannot solve. I've got an IReadOnlyList of classes that each have a bunch of fields. These fields have names (variable names) identical to a list of enums. Think that for each field that exists, an enum for it with the exact same name also exists (so object helloHi has an equivalent enum something { helloHi }).
What I've attempted to do is compare the two field names. If they are identical, perform a function on them. The problem is that the function needs to infer a T from the variable, and since reflection isn't able to pull that 'T' without some form of cast, it won't proceed.
This is the code:
public class Something() {
[BackgroundTask]
private void load(Overlay param_1, Config param_2) {
Children = new Drawable[] // is the IReadOnlyList
{
SomethingClass(param_1),
AnotherClass(param_2)
}
performTask(this, param_2);
}
}
public class Config {
public void task<U>(SomeEnums se, ValueType<U> value) // do the task
}
public class SomethingClass {
ValueType<double> someDouble = new ValueType<double>();
ValueType<int> someInt = new ValueType<int>();
public SomethingClass(Overlay overlay) //...
}
public enum SomeEnums {
someDouble,
someInt,
}
void performTask(Something the_class, Config the_config) {
// ... for each field within the_class, do (uses reflection)
field => {
foreach (SomeEnums enums in Enum.GetValues(typeof(SomeEnums)))
{
if (field.Name == enums.ToString()) {
the_config.task(enums, field.GetValue(null)); // cant infer 'U' from an 'object'
}
}
}
}
Technically, I could just do the config.task within the class where the types are known and visible, but I'd much prefer to automate it from here, so that it doesn't need 2-3 changes every time a new variable is created.
One of the strategies I am aware of is performing an if check within the performTask like such:
// performTask, field =>, foreach
{
if (field.FieldType == ValueType<double>)
config.task(enums, (ValueType<double>)field.GetValue(null));
} //etc
However, I don't like this method. It would just need to introduce more and more checks if I ever created more ValueType<> and if they aren't already being checked for. Would there be a better way to perform what I want?
As I mentioned in my comment, I can't quite tell what you really want to do. However, here's some code that should help you figure it out.
It uses reflection to get the fields of objects, look at the names of those fields (comparing them to the values/names associated with an enum type) and compare the values. I do a comparison to integer 5, but you could compare to anything (but, it appears that the integer type's implementation of IComparable.CompareTo throws if it's compared to something other than an int, so I check). Since you know the type of everything, this is easy to check (you don't have to compare to a fixed Type, you can use what is returned by GetType()).
I started with some auxiliary types:
public enum SomeEnums {
SomeDouble,
SomeInt,
}
public class Class1 {
public int SomeInt = 5;
public double SomeDouble = 3.14;
}
public class Class2 {
public int SomeInt = 5;
public double SomeDouble = 6.28;
}
and then added this:
public class EnumFields {
public List<object> Objects = new List<object> {
new Class1(),
new Class2(),
};
public void PerformTask () {
var enumVals = Enum.GetNames(typeof(SomeEnums));
foreach (var obj in Objects) {
var objType = obj.GetType();
var fieldInfos = objType.GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
//right around here I get lost. You have a list of objects (which has two instances right now),
//What are you comparing, that every field named SomeInt has the same value??
//Anyway, here's some code that should help you
foreach (var fieldInfo in fieldInfos) {
if (enumVals.Contains(fieldInfo.Name)) {
var fieldObj = fieldInfo.GetValue(obj);
var isSame = false;
if (fieldObj.GetType() == typeof(int)) {
var comparable = (IComparable)fieldObj;
var same = comparable.CompareTo(5);
isSame = (same == 0);
}
Debug.WriteLine($"Field: {fieldInfo.Name} of instance of {obj.GetType().Name} (Value: {fieldObj}) is equal to 5:{isSame}");
}
}
}
}
}
When I instantiate an EnumFields object and call PerformTask on it, I see this in the output:
Field: SomeInt of instance of Class1 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class1 (Value: 3.14) is equal to 5:False
Field: SomeInt of instance of Class2 (Value: 5) is equal to 5:True
Field: SomeDouble of instance of Class2 (Value: 6.28) is equal to 5:False
This should get you most of the way there. I realize it doesn't answer your question. Had I been able to figure out what you were asking, it probably would have.

Can I access a class variable with another variable?

i want to do a class constructor that takes a dicionary as parameter and initialize all the class variables that are listed as key in the dictionary, after of course a type conversion:
public class User
{
public int id;
public string username;
public string password;
public string email;
public int mana_fire;
public int mana_water;
public int mana_earth;
public int mana_life;
public int mana_death;
public User ()
{
}
public User(Dictionary<string,string> dataArray){
FieldInfo[] classVariablesInfoList = typeof(User).GetFields();
for(int i = 0; i < classVariablesInfoList.Length; i++)
{
if(dataArray.ContainsKey(classVariablesInfoList[i].Name)){
//missing code here :)
//need something like classVariable= dataArray["classVariablesInfolist[i].name"]; ?
}
}
}
}
but i can't find out how to do this!
Can you please help? :)
You can use the SetValue frunction from reflection:
FieldInfo f = classVariablesInfoList[i];
if (f.ReflectedType == typeof(int))
{
f.SetValue(this, Convert.ToInt32(dataArray[f.Name]));
}
else
{
f.SetValue(this, dataArray[classVariablesInfoList[i].Name]);
}
But it is a really uncommon way to do this with a dictionary. You should considder accessing the fields directly or add parameters to the constructor for any field. And fields should never be public - use properties instead.
The following will work if Convert.ChangeType() is able to handle the conversion. There are a lot of problems waiting to occur, for example handling numbers or dates where the string representation depends on the locale. I would really suggest to use usual typed constructor parameters or standard (de)serialization mechanism if possible. Or at least use a dictionary containing objects instead of strings to get rid of the conversion, again if possible.
public User(Dictionary<String, String> data)
{
var fields = typeof(User).GetFields();
foreach (field in fields)
{
if (data.ContainsKey(field.Name))
{
var value = Convert.ChangeType(data[field.Name], field.MemberType);
field.SetValue(this, value);
}
}
}
I would like to separate your problem into two parts.
1. Applying conversion
The FieldInfo type present a FieldType property that is the actual type of the field, using this Type we can use the non-generic ChangeType method of System.Convert, this method will be able convert some types to others. Luckily it support String to Int.
Usage:
Convert.ChangeType(OLD_VALUE, TARGET_TYPE);
2. Setting the field
The field info class has a SetValue method (FieldInfo.SetValue), it has two parameters, the first one is the current (ie. this) instance (or the instance you wish to change). the second is the new value you wish to set.
Putting it all together
[...]
var fieldInfo = classVariablesInfoList[i];
var name = fieldInfo.Name;
var targetType = fieldInfo.Type;
var value = Convert.ChangeType(dataArray[name], targetType);
classVariablesInfoList[i].SetValue(this, value);

C# pass class as string and cast it from array

I have few nested classes like "BlueprintsManager.WorkingStandardBlueprint", and ArrayList that have instances of them. I would like to pass them to a method as a parameter, eg:
private void ShowBlueprints(string class_str, ArrayList class_array)
{
// class_str would be passed as "BlueprintsManager.WorkingStandardBlueprint"
// how here I can access class_str as a class and cast class_array to it, to access some variables.
// for example, I need to access some BlueprintsManager.WorkingStandardBlueprint public variables.
}
I was messing around with Reflections, Generics but I still can't make it work.
Thank you.
You should use generics for this:
private void ShowBlueprints<T>(List<T> class_array)
{
for (BlueprintsManager.WorkingStandardBlueprint item in class_array)
{
if(typeof T is BlueprintsManager.WorkingStandardBlueprint)
{
Console.WriteLine(((BlueprintsManager.WorkingStandardBlueprint)item).whateverpropertyyouhavedefined);
}
}
}
Now you can call the method like this:
ShowBluePrints<BlueprintsManager.WorkingStandardBlueprint>(myblueprints);
EDIT In the comments the OP says that all properties are the same. This solution would work:
class BaseClass
{
string Name {get; set;}
int id {get; set;}
}
class BlueprintsManager
{
class WorkingStandardBlueprint : BaseClass
{
}
}
private void ShowBlueprints<T>(List<T> class_array) where T : BaseClass
{
for (T item in class_array)
{
Console.WriteLine(item.Name);
}
}
I think the question is why more than how. I didn't see many constructs like that.
You should use generics if that satisfies what you need.
And if you really need to construct types dynamically from an arbitrary list of itmes
1) Make a generic method (like suggested already)
interface IBlueprint
{
int ID {get;set;}
int Name {get;set;}
}
class MyClass
{
private void ShowBlueprints<T>(IEnumerableT> values) where T : IBlueprint
{
// access properties of IBlueprint
}
// I presume you 'know something' about your 'T'-s, have an interface -
// ...if you don't you should if possible
}
2) And call it like this (I typed in from memory but it should be correct)
MyClass myclass = new MyClass();
var values = // list of your blueprints
// if you don't have any in the list handle it and bail out
MethodInfo methodInfo = typeof(MyClass).GetMethod("ShowBlueprints");
MethodInfo methodInfoGeneric =
methodInfo.MakeGenericMethod(new[] { values.First().GetType() });
// or get your blueprint type from string if needed
methodInfoGeneric.Invoke(myclass, new object[] { values });
You cannot cast an object to a type that you only know by its string name. Therefore you also cannot access its fields that way. You have some options to access the fields of a type:
You know the exact type (or any of its base types or interfaces), so you can cast directly:
object first = class_array[0];
var blueprint = (BlueprintsManager.WorkingStandardBlueprint)first;
blueprint.MyProperty = 10;
You don't know the exact type but you are very sure it has a public property/field with a particular name. Note the dynamic keyword here, works in C# 4 and higher.
dynamic blueprint = class_array[0];
blueprint.MyProperty = 10;
You don't know the exact type, but you get a string with the type's name. And you don't know the exact property/field, but you get a string with the property's name. Then you can use reflection:
string typeName = "BlueprintsManager.WorkingStandardBlueprint";
string propertyName = "MyProperty";
var type = Assembly.GetExecutingAssembly().GetType(typeName);
var property = type.GetProperty(propertyName);
object first = class_array[0];
// Getter:
int result = (int)property.GetMethod.Invoke(first, null);
// Setter
property.SetMethod.Invoke(first, new object[] { 10 });
By the way, you shouldn't be using an ArrayList. It is a very old class from when generics didn't exist yet. Today you should use List<T>. For example, when you know all T implement an interface IBlueprint with the properties you want to use:
private void ShowBlueprints<T>(string classStr, List<T> classArray)
where T : IBlueprint
{
T blueprint = classArray[0];
blueprint.MyProperty = 10;
}
Or if you really have a list of objects of any type:
private void ShowBlueprints(string classStr, List<object> classArray);

Using reflection in C# to modify fields in a loop, extension method

After retrieving data from a database I find myself doing this to create a domain object from the data in a DataRow (in this case, a DVD):
DataRow drDvd = myDataTable.Rows[0];
Dvd myDvd = new Dvd();
myDvd.id = drDvd.Field<long>("id");
myDvd.title = drDvd.Field<string>("title");
myDvd.description = drDvd.Field<string>("description");
myDvd.releaseDate = drDvd.Field<DateTime>("releaseDate");
As I soon felt of course, I am doing this over and over in pseudo-code:
myDvd.field = drDvd.Field<field.type>(field.name);
And I wondered if I could get it into a loop, however I've never used reflection before. The code I tried is this:
Dvd aDvd = new Dvd();
Type t = aDvd.GetType();
FieldInfo[] fields = t.GetFields();
foreach (FieldInfo fi in fields)
{
fi.SetValue(aDvd, drDvd.Field<fi.FieldType>(fi.Name));
}
The problem is, as you may know, that the extension for the Field method of class DataRow does not accept a variable and needs to be explicitely filled in.
I am not that experienced in C# so I would like to pose the following two questions:
Is it good practice what I am trying to do?
How can I fill in the correct extension for Field<extension>(name)?
You'll need to get the method info for the generic method, and call invoke on it. This way you can pass in the generic type to it programmatically. I'm on my phone, but it should look something like this:
MethodInfo mField = typeof(Dvd).GetMethod("Field");
MethodInfo genericMethod = mField.MakeGenericMethod(new Type[] { fi.FieldType });
GenericMethod.Invoke(aDvd,new Object[]{fi.Name});
It is usually a bad practice to use reflection when it is not really necessary. Because reflection methods are checked at runtime rather than compile time, faulty code is harder to track, because the compiler can't check for errors.
If I were you, id have a look at the Entity Framework, because youre basically mapping database data to domain objects. http://msdn.microsoft.com/en-us/library/aa697427%28v=vs.80%29.aspx
This is one of the way of constructing and populating your domain object
DataRow drDvd = new DataRow();
Dvd aDvd = new Dvd();
Type type = typeof(Dvd);
foreach (FieldInfo fi in type.GetFields())
{
fi.SetValue(aDvd, drDvd[fi.Name]);
}
Your approach of using DataRow.Field may be round about. In you case, it is not applicable.
Alternatively you can think about using one of the Entity frameworks (NHibernate, Microsoft EF etc) in your application.
I would do a custom attribute. In doing an attribute you are stuck with your field name being the same as the database. I currently use this in my current applications and it works great. It is very similar to Entity SQL.
public class SqlMetaAttribute : Attribute
{
public string ColumnName { get; set; }
}
Then you have your class like this
public class Person
{
[SqlMeta(ColumnName = "First_Name")]
publice string FirstName { get; set; }
[SqlMeta(ColumnName = "Last_Name")]
publice string LastName { get; set; }
}
You would then have a helper class with the same kind of functions. In this case I am assuming the outside caller is looping through the datatable. Making it generic using the template T makes this really reusable. Rather than just having a "DVD" type implementation and coping and pasting for another.
public static T CreateObjectFromRow<T>(DataRow row)
{
var newObject = new T();
if (row != null) SetAllProperties(row, newObject);
return newObject;
}
public static void SetAllProperties<T>(DataRow row, T newObject)
{
var properties = typeof(T).GetProperties();
foreach(var propertyInfo in properties)
{
SetPropertyValue(row, newObject, propertyInfo);
}
}
public static void SetPropertyValue(DataRow row, T newObject, PropertyInfo propertyInfo)
{
var columnAttribute = propertyInfo.FindAttribute<SqlMetaAttribute>();
if (columnAttribute == null) return;
// If the row type is different than the object type and exception will be thrown, but that is
// okay because if that happens you have to fix your object you are using, or might need some
// more custom code to help you with that.
propertyInfo.SetValue(newObject, row.GetValue<object>(columnAttribute.ColumnName), null);
}
// Extension method for row.GetValue<object> used above
public static T GetValue<T>(this DataRow row, string columnName)
{
if (row.ColumnNameNotFound(columnName) || row.Table.Columns[columnName] == null || row[columnName] is DBNull)
{
return default(T);
}
return (T)row[columnName];
}

Categories