How do I compare FieldInfo's values of instances? - c#

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
myClass instance1 = new myClass();
myClass instance2 = new myClass();
FieldInfo[] fields = typeof(myClass).GetFields();
foreach (FieldInfo field in fields) if (field.GetValue(instance2) == field.GetValue(instance1)) Text = "Yes";
}
}
class myClass
{
public bool b = false;
public int i = 2;
}
Never returns "Yes".
EDIT: Without knowing beforehand what the types will be. So I can't have: (bool)field.GetValue(instance1).

You're using ==, which will be comparing the boxed values for any field where the type is a value type. Each time a value is boxed, it will create a new object, so == will never work like that. Use object.Equals instead:
foreach (FieldInfo field in fields)
{
if (object.Equals(field.GetValue(instance2), field.GetValue(instance1))
{
Text = "Yes";
}
}
(Using the static method here means it'll work even if the values are null.)

you are comparing the address of the two objects returned by FieldInfo.GetValue and since those addresses in memory are different, the == is never true.
try replacing the if with this:
if (field.GetValue(instance2).Equals(field.GetValue(instance1)))

Because field.GetValue(instance1) returns a "boxed" (object) version of the value, hence calling == you are only comparing two different references.
Try instead calling:
field.GetValue(instance2).Equals(field.GetValue(instance1))

Related

how to check whether a variable is a class or not in c#

I have a function that gets a object as a parameter
ex:
public static void doSomthingWithObject(object obj)
{
(.....)
}
and I want to check whether the object I got is a class or a simple variable (e.g if it requires a constructor to create) and if so, I want to get all the properties of that object so the code will look something like that:
public static void doSomthingWithObject(object obj)
{
if(objectIsClass())
{
object[] arr = obj.getAllPropeties
(.....)
}
else
{
(.....)
}
}
is that possible?
edit:
people found my definition to class variables and simple variables confusing. to make it clear, "simple variable" is a variable that can hold only one value, and to access this value you need to simply write "= var" while "class variable" can hold multiple values (unless it has 1-0 properties), and each one of its values can be accessed using get ex: = obj.prop (unless there is no get) each value in this type of variable is held by a property, and to define a new class property the new keyword must be used
In C#, everything that you see is either a class or a struct, (Int is struct for example).
So the question comes down to two things,
Do you want to know all the types which do not have a parameterless constructor (like int doesn't have)
To do that,
bool ObjectIsClass(object o)
{
return o.GetType().GetConstructor(new Type[0])!=null;
}
Do you want to Look for Primitive types
bool IsPrimitive(object o)
{
return o.GetType().IsPrimitive;
}
I think this should do it:
public static void DoSomthingWithObject(Object obj)
{
if (!obj.GetType().IsPrimitive)
{
Console.WriteLine("This is a class.");
var properties = obj.GetType().GetProperties();
foreach (var p in properties) {
Console.WriteLine(p);
}
}
else
{
Console.WriteLine("This is NOT a class.");
}
}
Not exactly sure what you mean by "get all properties". But this code will check if the indata is an instance of a non-primitive class and give you the name of all the properties. Perhaps it is a start.

Using Find() to find instance in collection based on enum value

I'm trying to find an object in a collection based on an Enum type property in the object. But it always returns NULL, even though I'm verifying that the collection does contain an object with the expected property value. Why is this?
public enum FooEnum
{
First, Second, Third, Fourth
}
public class Foo
{
public Enum EnumValue { get; set; }
}
class Program
{
static void Main(string[] args)
{
var fooCollection = new List<Foo>();
fooCollection.Add(new Foo() { EnumValue = FooEnum.First });
fooCollection.Add(new Foo() { EnumValue = FooEnum.Second });
fooCollection.Add(new Foo() { EnumValue = FooEnum.Third });
var fooSearchInstance = new Foo() { EnumValue = FooEnum.Fourth };
var fooFoundInstance = fooCollection.Find(f => f.EnumValue == fooSearchInstance.EnumValue); // NULL, for obvious reasons
fooSearchInstance.EnumValue = FooEnum.Second;
fooFoundInstance = fooCollection.Find(f => f.EnumValue == fooSearchInstance.EnumValue); // Also NULL - Why??
}
}
This boxes FooEnum.Second, and stores the resulting reference in fooSearchInstance.Enum:
fooSearchInstance.Enum = FooEnum.Second;
This boxes FooEnum.Second and uses reference identity to compare it with f.Enum:
f.Enum == fooSearchInstance.Enum
You're boxing twice, so you've created separate objects - reference identity won't work for you. The code you've got is a more complicated form of this:
Enum x = FooEnum.Second; // Box once
Enum y = FooEnum.Second; // Box again, to a separate object
Console.WriteLine(x == y); // False, because they're different references
You could use Equals instead:
f => Equals(f.Enum, FooEnum.Second)
... although boxing on every iteration isn't ideal. You could box just once before the Find call:
Enum boxed = FooEnum.Second;
fooFoundInstance = fooCollection.Find(f => Equals(f.EnumValue, boxed));
Even this isn't ideal though, in my opinion. I very rarely find it useful to have a field or property of type Enum. It may be necessary in your case (we don't have enough context to know) but in general it would better to use the concrete enum type if at all possible.

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.

How to fix null subclasses instances with reflection

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).

How to pass string into collection by reference?

It seems that string is reference, but copied by value
List<string> data = new List<string>();
string s = "a";
data.Add(s); // copy by value??
s = "b";
Console.WriteLine(data[0]); // "a"
It also seems that class is reference copied by reference
class boxstring { public string val; }
List<boxstring> data = new List<boxstring>();
boxstring s = new boxstring { val = "a" };
data.Add(s); // copy by reference, ok
s.val = "b";
Console.WriteLine(data[0].val); // "b"
Is there a simpler way to pass string into collection by reference without wrapping it into class? I'd like to pass object member into collection and if the object is changed, the collection should be also changed without writing extra code.
Strings are immutable, if you want the objects in your container to change, they must be mutable. Therefore, you must wrap them in a class like you did.
Strings are immutable. Whenever you assign new value to string variable, a new instance is created everytime that's why you can't see the updates in your collection.
However, .NET already provide mutable counterpart of string i.e. "StringBuilder". This will work for you -
List<StringBuilder> data = new List<StringBuilder>();
StringBuilder s = new StringBuilder("a");
data.Add(s);
s.Clear();
s.Insert(0, "b");
Console.WriteLine(data[0]); // "b"
Here's an idea to make you code simpler :
public MyString
{
public string Value{get;set;}
public MyString(string value)
{
Value=value;
}
public static implicit operator MyString(string value)
{
return new MyString(value);
}
public static implicit operator string(MyString mystring)
{
if(mystring==null) return null;
return mystring.Value;
}
then you can use MyString object whenever you want to have string by reference.since we have these implicit operator in place you can use MyString instead of string
You cannot pass intrinsic data-types by reference, they are always passed by value.
Intrinsic types include basic types like Int32, String, Bool, etc..

Categories

Resources