How to fix null subclasses instances with reflection - c#

I have a main instance storing various subclasses instances with options inside.
Class MainClass
{
public bool b;
public int i;
public List l = new List();
Class SubClass1
{
...
}
public SubClass1 sub1 = new SubClass1();
Class SubClass2
{
...
}
public SubClass2 sub2 = new SubClass2();
}
now, when starting all class are correctly instatiated, then some options are set, and the result is serialized.
The problem arises when (for various reasons) I have to change name of the instances.
E.g. SubClass2---->SubClassB
therefore when de-serializing SubClassB is obviously null.
So I have to fix this drawback and I have tought about reflection.
Something like [pseudocode]
foreach(var subclass in MainClass)
{
if(subclass is null)
{
Type subClassType = typeof(subclass);
subclass = new subClassType();
}
}
Thank you in advance for any help.
---ADD for completeness the solution from thehennny's hint---
private void CheckAndFixNullInstances()
{
easyRunData.OptionsReport = null;
Type fieldsType = typeof(EasyRunBinSerializableData);
FieldInfo[] fields = fieldsType.GetFields(BindingFlags.Public | BindingFlags.Instance);
for (int i = 0; i < fields.Length; i++)
{
string str = fields[i].Name + " " + fields[i].GetValue(easyRunData);
if (fields[i].GetValue(easyRunData) == null)
{
string strFieldType = fields[i].FieldType.AssemblyQualifiedName;
Type t = Type.GetType(strFieldType);
object item;
item = Activator.CreateInstance(t);
fields[i].SetValue(easyRunData, item);
}
}
}

I am not familiar with the xml deserialization process, but what you basically want is to loop over all fields of a particular object and fill all null fields with a new object of the fields type.
To get all fields of the type you have to use a suitable Type.GetFields overload.
Then you can loop over the FieldInfo objects you got and call the FieldInfo.GetValue Method.
After doing the null check you can then create a new object with the Activator.CreateInstance Method by passing the FieldInfo.FieldType Property as parameter and store it in the field using the FieldInfo.SetValue Method.

The above won't work as you cannot get any type information from a null object. Essentially when you serialise the object you would want to store the fully qualified name of the class you expect. Then when you deserialise it out you can read that value. If the object is "null", you can create an instance of the fully qualified type.
NB: Note "null" in quotes because here "null" is used to semantically mean something which is not there and not necessarily a null object.
NBB: I have solved exactly that problem in a Github project I have which you are welcome to use (https://github.com/ruskindantra/extensions/blob/master/RuskinDantra.Extensions/DataStructures/XmlSerializableInterfaceList.cs).

Related

C# How to init property (without setter) by reflection

Task:
Serialize a list of objects into a byte[] using protobuf.
Without reflection all is good
.proto
message MyObject{
int32 id = 1;
int32 value = 2;
}
message MyObjects {
repeated MyObject objects = 1;
}
.cs
public static byte[] ToByteArray(List<MyObject> obj) {
var objects = new MyObjects {
Objects = {obj}
};
return objects.ToByteArray();
}
Since I need to serialize many different types in this way, I want to write a universal method using reflection.
Problem:
Protobuf itself generates entities and properties for them, but it does not create a setter for RepeatedField, which means that I can not set the value using GetProperty("Objects")?.SetValue(objects, obj). System.ArgumentException: Set Method not found for 'Objects'
.cs (protobuf generated)
public pbc::RepeatedField<global::Test.MyObject> Objects {
get { return objects_; }
}
.cs
public static byte[] ToByteArray<T, E>(List<T> obj) where T : IMessage where E : IMessage {
var objects = Activator.CreateInstance<E>();
objects.GetType().GetProperty("Objects")?.SetValue(objects, obj);
return objects.ToByteArray();
}
Question:
How to use reflection to set values ​​for a property during object creation, just as I do it without reflection?
How to write this "new MyObjects {Objects = {obj}}; (where obj: IEnumerable)" using reflection
Various conclusions:
I noticed that filling properties that do not have a setter is only possible for collections and only when creating an object.
Most likely I need an alternative way to instantiate the class. Activator.CreateInstance() is not fulfilling my task.
When we do this:
var x = new Thing
{
SomeProperty = "x",
SomeOtherProperty = 1
}
we aren't setting the values during object creation. This is the equivalent of:
var x = new Thing();
x.SomeProperty = "x";
x.SomeOtherProperty = 1;
In both cases the properties are set after the object is instantiated by setting their properties. An easy way to verify this is to try using the syntax from the first example to set a property that doesn't have a setter. It won't compile. You'll see this error:
Property or indexer 'Thing.SomeProperty' cannot be assigned to -- it is read-only.
In other words, the object, as defined, does not provide a way to set the Objects property.
The question is whether you really need to set the property. Quite likely you only need to add items to the collection.
Doing this with reflection is still really ugly. I don't recommend this at all. This is a crude version. It can fail at runtime for all sorts of reasons.
public static byte[] ToByteArray<T, E>(List<T> itemsToAdd) where T : IMessage where E : IMessage
{
// create an instance of the object
var created = Activator.CreateInstance<E>();
// Find the "Objects" property. It could be null. It could be the wrong type.
var objectsProperty = typeof(E).GetProperty("Objects");
// Get the value of the objects property. Hopefully it's the type you expect it to be.
var collection = objectsProperty.GetValue(created);
// Get the Add method. This might also be null if the method doesn't exist.
var addMethod = collection.GetType().GetMethod("Add");
// invoke the Add method for each item in the collection
foreach(var itemToAdd in itemsToAdd)
{
addMethod.Invoke(collection, new object[] { itemToAdd });
}
return created.ToByteArray();
}
Unless we're forced to, we really don't want to do that. I don't know what your IMessage type look like.
Does it have the Objects property?
In that case you could just do this:
public static byte[] ToByteArray<T, E>(List<T> itemsToAdd)
where T : IMessage
where E : IMessage, new()
{
var created = new E();
foreach (var itemToAdd in itemsToAdd)
{
created.Objects.Add(itemToAdd);
}
// or skip the foreach and just do
// created.Objects.AddRange(itemToAdd);
return created.ToByteArray();
}
I'm guessing about whether your interface has that property. But if at all possible, you're better off doing this with generic constraints than with reflection. This way your code is checked for most possible errors when it compiles, as opposed to running it and having it blow up because this or that property or method doesn't exist, is wrong, etc.
The new() constraint simply means that E must be a type with a default constructor, which means that in order for it to compile, E must be a type you can create without passing anything to the constructor. (Without that constraint, new E() won't compile.)
Without that constraint even Activator.CreateInstance might fail because the the type might not have a default constructor.
Scott's answer solves the problem, but I used a shortened solution in the end
private static byte[] ToByteArray<T, E>(IEnumerable<T> obj) where T : IMessage where E : IMessage, new() {
var objects = new E();
(objects.GetType().GetProperty("Objects")?.GetValue(objects) as RepeatedField<T>)?.AddRange(obj);
return objects.ToByteArray();
}

How to access methods of subclass in object array C#?

How can I set/get the value of an object in an object array?
Currently I get:
"object does not contain a definition for 'value' and no extension method"
Example C#;
public class myObjClass
{
public int value = 5;
}
public class myObjClass2
{
public float[] pos = new float[2];
}
public void test()
{
myObjClass myObj = new myObjClass();
myObjClass2 myObj2 = new myObjClass2();
object[] objArr = new object[2];
objArr[0] = myObj;
objArr[1] = myObj2;
Debug.Print(myObj.value.ToString());
Debug.Print(objArr[0].value.ToString()); // how?
}
Its because a generic object does not have the property value your class myObjClass has. To fix this you could cast the item to your class like so:
((myObjClass)objArr[0]).value.ToString()
Only do this ^ if you are sure of the type
Instead you could also check it first:
With as:
var item = objArr[0] as myObjClass;
if( item != null ) // Item will be null if its not a 'myObjClass'
{
//Do stuff with item
}
Or with is:
if( objArr[0] is myObjClass )
{
var item = (myObjClass)objArr[0];
//Do stuff with item
}
When using an object array you have to cast to the real type (here: myObjClass) before accessing the fields:
You can access the object like this
((myObjClass)objArr[0]).value
but I would not recommend. Can´t you have your array to be the concrete type
var array = new myObjClass[42]
A compact safe alternative to retrieve the value is
(objArr[0] as myObjClass)?.value
You need to cast object to known type which is myObjClass, like:
((myObjClass)objArr[0]).value.ToString();
Or you can use reflection
var valueString = objArr[0].GetType().GetProperty("value").GetValue(objArr[0]);
Debug.Print(valueString.ToString());
Hope helps,
Technically you can put it as
Debug.Print((objArr[0] as myObjClass)?.value.ToString());
We try casting objArr[0] as myObjClass and if succeed get value and turn it to string. If objArr[0] is not myObjClass we return null as a string
However, a much better way is to implement ToString() in both classes of interest:
public class myObjClass
{
public int value = 5;
public override string ToString() {
// When debugging we want to know "value"
return value.ToString();
}
}
public class myObjClass2
{
public float[] pos = new float[2];
public override string ToString() {
// When debugging we want to know "pos[0]" and "pos[1]" values
return $"{pos[0]} : {pos[1]}";
}
}
And then put an easy
// Just print out debug info (we don't want to know the actual objArr[0] class)
Debug.Print(objArr[0].ToString());
You have a single object, that indeed is an instance of myObjClass, and has a value field, but you have two references to it.
One (myObj) is known to the compiler to be of type myObjClass, and it can guarantee that it has a value field.
The other (objArr[0]) is only known to the compiler to be of type object, and it cannot guarantee that it has a value field.
For example, you could do:
objArr[0] = (random.Next() > 0.5) : myObj ? myObj2
where we're gonna decide at runtime, based on the value of a random number, which will be the type of the actual object at objArr[0].
So, if this was allowed, half of the time objArr[0].value would be correct, and half of the time it will be an error.

Check if two variables are of the same when the type is dynamic and both variables are the derivatives of same base class

Is it possible to check if the list contains an object of given (but dynamic) type, derrived from same basic abstract class?
The main problem is not about the list, but about comparing types itself.
In single variables and static variables, it's easy:
if(someVariable is int)
Checking the list with static type is also easy, like:
SomeList.OfType<int>().Any()
or
(from _Object in SomeList.OfType<int> where _Object is int select _Object).Count() == 0
but I cant't handle it if the type I want to check is dynamic, f.e. passed as method parameter:
abstract class BasicClass;
class DerivativeOne : BasicClass { }
class DerivativeTwo : BasicClass { }
// in main:
List<BasicClass> _List = new List<BasicClass>();
DerivativeOne a = new DerivativeOne();
DerivativeTwo b = new DerivativeTwo();
DerivativeOne c = new DerivativeOne();
if(!CheckIfTypeExistsInList(a, _List)
{
_List.Add(a);
}
if(!CheckIfTypeExistsInList(b, _List)
{
_List.Add(b);
}
if(!CheckIfTypeExistsInList(c, _List)
{
_List.Add(c); // this is what I don't want to happen,
// because I already have one object of type DerivativeOne in my list.
}
// the function:
bool CheckIfTypeExistsInList(BasicClass pObject, List<BasicClass> pList)
{
/// few attempts:
pList.OfType<(pObject.GetType()>().Any(); // attempt one, error
return (from _Object in SomeList.OfType<(pObject.GetType())> where _Object is int select _Object).Count() == 0; // attempt two, error
}
PS. I am aware that the code doesn't look neat, but I tried to show just the problem itself, skipping extra logic and stuff.
PS2. I am aware that the solution to the problem would be just to put some attribute to BasicClass and make each derivative to have unique value of the attribute, but still - I'm not looking for another route to solve the problem, I'm just interested if it's possible to do it "this" way.
When the type is known only at runtime, you cannot use it in a generic without using reflection. However, your task is simpler than that - you can use type equality to achieve the results that you want:
Type targetType = pObject.GetType();
if (SomeList.Any(o => targetType.Equals(o.GetType()))) {
...
}

How to cast object at runtime, while getting type information at runtime

Please have a look at below code.
Issue is at following loc.
MyClassExample obj2 = lstObjectCollection[0] as type;
I want to type cast an object of list to its type. But type will be given at runtime.
How can we cast an object, knowing its type at runtime?
class RTTIClass
{
public void creatClass()
{
// Orignal object
MyClassExample obj1 = new MyClassExample {NUMBER1 =5 };
// Saving type of original object.
Type type = typeof(MyClassExample);
// Creating a list.
List<object> lstObjectCollection = new List<object>();
// Saving new object to list.
lstObjectCollection.Add(CreateDuplicateObject(obj1));
// Trying to cast saved object to its type.. But i want to check its RTTI with type and not by tightly coupled classname.
// How can we achive this.
MyClassExample obj2 = lstObjectCollection[0] as type;
}
public object CreateDuplicateObject(object originalObject)
{
//create new instance of the object
object newObject = Activator.CreateInstance(originalObject.GetType());
//get list of all properties
var properties = originalObject.GetType().GetProperties();
//loop through each property
foreach (var property in properties)
{
//set the value for property
property.SetValue(newObject, property.GetValue(originalObject, null), null);
}
//get list of all fields
var fields = originalObject.GetType().GetFields();
//loop through each field
foreach (var field in fields)
{
//set the value for field
field.SetValue(newObject, field.GetValue(originalObject));
}
// return the newly created object with all the properties and fields values copied from original object
return newObject;
}
}
class MyClassExample
{
public int NUMBER1 {get; set;}
public int NUMBER2{get; set;}
public int number3;
public int number4;
}
The pattern I normally use is the is operator which will tell whether your object is a particular type. This will work if you already kinda sorta know which objects you will be using
Object myObject
if(myObject is Obj1)
// do obj1 stuff
else if(myObject is Obj2)
// do stuff with obj2
I've never had it come up where i had to operate on more than a handful of different types and treat them all specially, so this is what i normally do.
You can easily get all the objects of a certain type in the list using the OfType<T> extension method:
lstObjectCollection.OfType<MyClassExample>()
If the type is only known at runtime, you can do this:
lstObjectCollection.Where(o => o.GetType() == type)

How to access the Index Of A Generic.List By Reflection?

ok, ive a class and i pass an object as property.
the object that i pass is a List<X>
in my class im trying to access the Object index by reflection BUT I CAN'T!!!
Example:
this class works i just wrote down the part i want to show you and i need help.
class MyClass
{
private object _recordSet;
public object RecordSet
{
get { return _recordSet; }
set { _recordSet = value; }
}
public string Draw()
{
system.reflection.Assembly asem = system.reflection.Assembly.getAssembly(_dataSource.GetType());
object instance;
instance = asem.CreateInstance(_dataSource.GetType().UnderlyingSystemType.FullName);
//to access de Count of my List
int recordcount = int.Parse(_dataSource.GetType().GetProperty("Count").GetValue(_dataSource,null));
//i need to do a
for(int cont = 0; cont < recordCount; cont++)
{
_dataSource[cont].Name; // <-- THIS PART IS NOT WORKING!!! because i cant access the Index Directly.... WHAT TO DO!! ???
}
}
}
If you are using reflection (and hence lots of object), why not just cast as an IList (non-generic) instead?
i.e.
IList list = (IList)actualList;
object foo = list[17];
Also - for your original code with Count, you don't mean int.Parse - you should just cast (since we expect Count to be an int).
Just cast your object to a list first, you don't need reflection here.

Categories

Resources