I think this could be a common question but I failed to find any post about it.
I'm writing some pseudo code below. If you want to compile the code, please just ignore this post.
So say two classes: one base class and one child class. NOTE: both classes have override Equals() and GetHashCode() function to ensure equality with same property.
public class A // A has a string property of name
public class B:A // B has a string property of title
var a = new A{name = "bob"};
var b = new B{name = "bob", title = "em"};
Some code have a dictionary based on A
var dict = new Dictionary<A>();
Doing some adding stuff, for instance,
dict.Add(a);
However, the lookup function will raise KeyNotFoundException if i use a derived class searching with/o type cast
dict[b];
Dictionary will calculate the hashcode of B instead of A and raised the exception according to that.
A simple and awkward solution is to create a new instance of A based on B's property.
dict[new A{name = b.name}];
I wonder if there is any better solution?
Try creating an EqualityComparer, and pass an instance of it in the constructor of the dictionary.
class AEqualityComparer : IEqualityComparer<A>
{
public bool Equals(A x, A y)
{
return x.Equals(y);
}
public int GetHashCode(A obj)
{
return obj.GetHashCode();
}
}
var dict = new Dictionary<A, object>(new AEqualityComparer());
You're confusing storing objects in a list, with the key of a dictionary. If you use objects as a key, you need to supply a reference to the same object - not just one with the same properties.
If you do this:
dict.Add(new A{name = "bob"}, someData);
and then do this
var result = dict[new A{name = "bob"}];
you'll get 'key not found' because the two new As are different objects.
Related
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();
}
My question is basically the opposite of Dictionary.ContainsKey return False, but a want True and of "the given key was not present in the dictionary" error when using a self-defined class as key:
I want to use a medium-sized class as the dictionary's key, and the dictionary must compare the keys by reference, not by value equality. The problem is, that the class already implements Equals() (which is performing value equality - which is what not what I want here).
Here's a small test class for reproduction:
class CTest
{
public int m_iValue;
public CTest (int i_iValue)
{
m_iValue = i_iValue;
}
public override bool Equals (object i_value)
{
if (ReferenceEquals (null, i_value))
return false;
if (ReferenceEquals (this, i_value))
return true;
if (i_value.GetType () != GetType ())
return false;
return m_iValue == ((CTest)i_value).m_iValue;
}
}
I have NOT yet implemented GetHashCode() (actually I have, but it only returns base.GetHashCode() so far).
Now I created a test program with a dictionary that uses instances of this class as keys. I can add multiple identical instances to the dictionary without problems, but this only works because GetHashCode() returns different values:
private static void Main ()
{
var oTest1 = new CTest (1);
var oTest2 = new CTest (1);
bool bEquals = Equals (oTest1, oTest2); // true
var dict = new Dictionary<CTest, int> ();
dict.Add (oTest1, 1);
dict.Add (oTest2, 2); // works
var iValue1 = dict[oTest1]; // correctly returns 1
var iValue2 = dict[oTest2]; // correctly returns 2
int iH1 = oTest1.GetHashCode (); // values different on each execution
int iH2 = oTest2.GetHashCode (); // values different on each execution, but never equals iH1
}
And the hash values are different every time, maybe because the calculatation in object.GetHashCode() uses some randomization or some numbers that come from the reference handle (which is different for each object).
However, this answer on Why is it important to override GetHashCode when Equals method is overridden? says that GetHashCode() must return the same values for equal objects, so I added
public override int GetHashCode ()
{
return m_iValue;
}
After that, I could not add multiple equal objects to the dictionary any more.
Now, there are two conclusions:
If I removed my own GetHashCode() again, the hash values will be different again and the dictionary can be used. But there may be situations that accidentally give the same hash code for two equal objects, which will cause an exception at runtime, whose cause will for sure never be found. Because of that (little, but not zero) risk, I cannot use a dictionary.
If I correctly implement GetHashCode() like I am supposed to do, I cannot use a dictionary anyway.
What possibilities exist to still use a dictionary?
Like many times before, I had the idea for a solution when writing this question.
You can specify an IEqualityComparer<TKey> in the constructor of the dictionary. There is one in the .net framework, but it's internal sealed, so you need to implement your own:
Is there any kind of "ReferenceComparer" in .NET?
internal class ReferenceComparer<T> : IEqualityComparer<T> where T : class
{
static ReferenceComparer ()
{
Instance = new ReferenceComparer<T> ();
}
public static ReferenceComparer<T> Instance { get; }
public bool Equals (T x, T y)
{
return ReferenceEquals (x, y);
}
public int GetHashCode (T obj)
{
return System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode (obj);
}
}
As per the question, how do I create a Dictionary in C# where the key is say, an integer, but the values are classes that I can call the constructor for by using just the value in the Dictionary? Each of the classes are derived from an abstract class, and they take the same parameters, so I feel like I should be able to store the resulting reference to the new class object in a variable of the abstract class type.
That being said, a Dictionary's value set is usually filled with references to objects, not types themselves. As a quick example, here's what I'm trying to do:
abstract class BaseObject {
int someInt;
}
class ObjectA : BaseObject {
ObjectA (int number) {
someInt = number;
}
}
class ObjectB : BaseObject {
ObjectB (int number) {
someInt = number;
}
}
And I want to be able to do the following:
Dictionary<int, ???????> objectTypes = new Dictionary<int, ???????>();
objectTypes.Add(0, ObjectA);
objectTypes.Add(1, ObjectB);
So I can eventually:
BaseObject newObjectA, newObjectB;
newObjectA = new objectTypes[0](1000);
newObjectB = new objectTypes[1](2000);
The syntax is probably quite different, but I hope I at least got across what I'm trying to accomplish.
Are you looking for something like this?
var objectTypes = new Dictionary<int, Func<int, BaseObject>>();
objectTypes[0] = input => new ObjectA(input);
objectTypes[1] = input => new ObjectB(input);
objectTypes[0](1000);
objectTypes[1](2000);
Here instead of storing object, I store a Func to generate each concrete object
Dictionary<int, Type> objectTypes = new Dictionary<int, Type>();
objectTypes.Add(0, typeof(ObjectA));
objectTypes.Add(1, typeof(ObjectB));
var newObjectA = (BaseObject)Activator.CreateInstance(objectTypes[0], new object[] {1000});
Documentation for Activator.CreateInstance
Leaving this here as it is just another way to do it, but Phuong's answer is a much cleaner and customizable approach at compile time.
The key difference is if you want something dynamic at runtime or compile time. If it's all compile time, I suggest the Func method. If your types are unknown at compile time, then you will want to use this way as there are many ways to dynamically instantiate a class using Activator.CreateInstance.
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()))) {
...
}
So for example I have two classes:
Class A
{
string property1;
string property2;
}
Class B : A
{
string property3;
string property4;
....
}
So B inherits class A's properties. They are sitting in a list, that is sitting in a dictionary
Dictionary <string, List<A>> myDictionary = new Dictionary<string, List<A>>();
List<A> myList = new List<A>();
There is one Dictionary, containing many List's, that all contain a mix of Class A & B objects.
While looping through, I am trying to access some properties from Class B objects, I have an if statement to find them but the program still thinks they are of type Class A and throws an error when I try and use a property3 or property4. For example:
string key = string key in dictionary;
string index = object position in list;
myDictionary[key][index].property3.someMethod();
Is there a way to tell the program that this is a class B object and allow the properties 3 & 4 to be used?
Cast the object safely as a B-type object, then check for null
var obj = myDictionary[key][index];
var bObj = obj as B;
if (bObj != null)
{
bObj.someMethod();
}
Although, I would also probably say it seems like your design is off. Ordinarily, I wouldn't expect something like this. Normally, if you're using inheritance, you'd want a design that allows them to be used interchangeably. For example, you might implement the behavior on A as a no-op, but override it on B to actually do something. This would make it so that consuming classes need not care whether the "A" thing is really an A or a B instance.