Retrieve NHibernate.Collections.Generic.PersistentGenericBag as IList<T> using Reflection - c#

I'm attempting to compare a transient object graph to an NHibernate-persisted object graph. Unfortunately my code breaks where properties of type IList<T> are concerned. The code below works fine with instances of List<T>, because List<T> implements both IList<T> AND IList. Unfortunately, NHibernate's PersistentGenericBag only implements IList<T>.
IList list1 = (IList)prop1.GetValue(object1, null);
IList list2 = (IList)prop2.GetValue(object2, null);
If either object1 or object2 is a PersistentGenericBag, I get an error such as:
System.Reflection.TargetInvocationException : Exception has been thrown
by the target of an invocation.
----> System.InvalidCastException : Unable to cast object of type
'NHibernate.Collection.Generic.PersistentGenericBag`1[MyNamespace.MyClass]'
to type 'System.Collections.Generic.List`1[MyNamespace.MyClass]'.
Is there a reliable way to retrieve the PersistentGenericBag instance as an IList<T> using reflection?
I had hoped the popular Compare .NET Objects class would help, but it fails with the exact same error.
Edit: All the answers below are on the right track. The problem was that the getter on the problematic IList<T> property was attempting a cast to List<T>, which obviously can't be done to a PersistentGenericBag. So, my fault for the misguided question.

EDIT: never mind. commenter is right, you CAN go straight to IList. I was focusing one the question as stated a little too hard to see the obvious even as I was coding the answer. doh!
Ok, you just need to dig a little bit deeper.
The base class of PersistentGenericBag is PersistentBag, which does implement IList.
var prop1 = typeof (Customer).GetProperty("Invoice");
// if you need it for something...
var listElementType = prop1.PropertyType.GetGenericArguments()[0];
IList list1;
object obj = prop1.GetValue(object1, null);
if(obj is PersistentBag)
{
list1 = (PersistentBag)obj;
}
else
{
list1 = (IList)obj;
}
foreach (object item in list1)
{
// do whatever you wanted.
}
Tested and works for bags. Take it to the logical conclusion for other list/set/collection types that you might encounter.
So, the short answer is; If you KNOW it is a bag, you can just cast the object first to PersistentBag and then to IList...
IList list = (PersistentBag)obj;
If you DONT KNOW then use some conditional logic as shown.

You don't need an IList to compare two collections.
Cast to IEnumerable instead.

Related

Casting object to List<(Enum, string)> issue

I am creating a converter by implementing IMultiValueConverter with *Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
I am passing a List<(SomeEnumType, string)> tuple.
via MultiBinding and on the converter side I would like to cast but it throws a casting error.
I tried :
var result = (List<(Enum, string)>)values[1];
but I got this casting issue:
'Unable to cast object of type 'System.Collections.Generic.List1[System.ValueTuple2[Vasco.Basics.Contracts.CoreConfigurations.Enums.ApplicationType,System.String]]' to type 'System.Collections.Generic.List1[System.ValueTuple2[System.Enum,System.String]]'.'
It is strange because If I pass only one element of SomeEnumType and try to case like (Enum)values[1] casting works well.
When I pass a List<SomeEnumType> and try to cast like (List<Enum>)values[1] does not work already.
Thank you in advance!
When I pass a List and try to cast like (List)values1 does not work already.
You generally aren't allowed to cast generic collections like List<T> or IEnumerable<T> to other types. This comes down to how C# and the compiler handle generics and something called Covariance and contravariance. This is an incredible complicated topic, at least for me, so I won't bogg you down with the fine details.
Consider the following situation.
List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;
This may seem pretty natural, especially if you try to explicitly cast the strings list such as (List<object>)strings, but this wont compile and that is a good thing! It protects you from doing silly things, like for example:
List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;
objs.Add(1.29d);
this may seem like it's only tangentially related to you question, but this is really important, and is the exact reason you cant cast a collection to a different kind of collection, even if you know that they're very similar.
When we add that double to the objs list (assuming that this would compile, it doesn't), what were doing effectively is adding a double to a List<string> which would break everything about how strongly typed languages such as C# work.
It is strange because If I pass only one element of SomeEnumType and try to case like (Enum)values1 casting works well.
The reason you can do this, but not collections, is becuase with a single object the compiler can check to see if there is a valid conversion and do the conversion for you manually. Unlike with collections where the compiler, if it did the same thing as it did with single objects, it would add things to collections that may not match the type that was constrained when that collection was initialized.
Credit to John Skeet for this explanation, Ch4.4.1 ISBN 9781617294532
In general you cannot cast Lists like this - because what you are actually trying to do is cast each item in the list, rather than the list itself. Therefore you'd need to loop through and cast each item individually, like so:
var input = new List<(SomeEnumType, string)>();
// now add items to the input list
var result = new List<(Enum, string)>();
foreach (var element in input)
{
result.Add(
((Enum)element.Item1, element.Item2)
);
}
Remember, a tuple is not a single element but a wrapper for multiple elements, which each need casting.
Or, you could use a tool that allows you to 'map' types, e.g. Mapster or AutoMapper - I personally prefer Mapster.
using Mapster;
var input = new List<(SomeEnumType, string)>();
// now add items to the input list
var result = input.Adapt<List<(Enum, string)>>();
// Adapt<>() is an extension method provided by Mapster

Contravariance/Covariance, why can't cast this?

Let's face it, I am still having some difficulties to understand the constrains when it's time to use covariance and contravariance in generics.
I wonder, why if I have this:
public interface IFasterListCov<out T>
{}
public interface IFasterListCon<in T>
{}
public class FasterList<T> : IList<T>, IFasterListCov<T>, IFasterListCon<T>
The third cast fails:
public void QueryNodes<T>() where T:INode
{
//somehow I can convert IFasterListCon<INode> to IFasterListCon<T>
IFasterListCon<INode> nodes = (IFasterListCon<INode>)_nodesDB[type];
//I guess this works because _nodesDB[type] is indeed a FasterList<T> object
//note: I am wrong, I can cast whatever INode implementation, not just T, which made me very confused :P
IFasterListCon<T> nodesT = (IFasterListCon<T>)nodes;
//I can't cast IFasterListCon<T> back to FasterList<T>
FasterList<T> nodeI = nodesT as FasterList<T>; //null
}
Dictionary<Type, IFasterListCov<INode>> _nodesDB;
to be clear _nodesDB[type] is a FasterList<T> declared through IFasterListCov<INode>
MyType : IMyType does not make Generic<IMyType> and Generic<MyType> related in any way.
In your particular case it is likely that nodesT is FasterList<Node> which is not FasterList<INode>.
Note that this conversion work for interface which support variance (co/contra) when you can specify in/out as you see in successful conversion to interface. See one of many questions for details - i.e. Generic Class Covariance.
There is also excellent answer about List co-variance - C# variance problem: Assigning List<Derived> as List<Base> which shows that List<Derived> and List<Base> can't be cast between each other:
List<Giraffes> giraffes = new List<Giraffes>();
List<Animals> animals = new List<Animals>() {new Lion()};
(giraffes as List<Animals>).Add(new Lion()); // What? Lion added to Girafes
Giraffe g = (animals as List<Giraffes>)[0] ; // What? Lion here?
In the scenario where you're calling QueryNodes<MyNode>, in order for your last cast to get a non-null value, the actual instance that you get with _nodesDB[type] must be a FasterList<MyNode>. It's not good enough for it to be FasterList<SomeOtherMostlyCompatibleNode>.
The runtime is very strict about types, it keeps track of the actual runtime types of everything involved, it's not good enough for the data types to be similar, or for you to only have MyNode objects populating your FasterList<SomeOtherMostlyCompatibleNode>, or anything else. If the types are not exactly what they should be, you need to do some sort of programmatic conversion, not just cast.

How to implement ICollection<T> on an IEnumerable<T>

I would like to know how to program what Microsoft is suggesting from the MSDN guidelines for collections, which state the following:
AVOID using ICollection<T> or ICollection as a parameter just to access the
Count property. Instead, consider using IEnumerable<T> or IEnumerable and
dynamically checking whether the object implements ICollection<T> or ICollection.
In short, how do I implement ICollection on an IEnumerable? Microsoft has links all over that article, but no "And here is how you do this" link.
Here is my scenario. I have an MVC web app with a grid that will paginate and have sorting capability on some of the collections. For instance, on an Employee administration screen I display a list of employees in a grid.
Initially I returned the collection as IEnumerable. That was convenient when I didn't need to paginate. But now I'm faced with paginating and needing to extract the Count of employee records to do that. One workaround was to pass an employeeCount integer by ref to my getEmployeeRecords() method and assign the value within that method, but that's just messy.
Based on what I've seen here on StackOverflow, the general recommendation is to use IEnumerable instead of ICollection, or Collection, or IList, or List. So I'm not trying to open up a conversation about that topic. All I want to know is how to make an IEnumerable implement an ICollection, and extract the record count, so my code is more aligned with Microsoft's recommendation. A code sample or clear article demonstrating this would be helpful.
Thanks for your help!
One thing to note is that if you use LINQ's Count() method, it already does the type checking for you:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
checked
{
while (e.MoveNext()) count++;
}
}
return count;
}
Initially I returned the collection as IEnumerable.
Well there's half your problem. Return types should be as explicit as possible. If you have a collection, make the return type that collection. (I forget where, but this is mentioned in the guidelines.)
Based on what I've seen here on StackOverflow, the general recommendation is to use IEnumerable instead of ICollection, or Collection, or IList, or List.
Some developers have an obsession with casting everything as IEnumerable. I have no idea why, as there is no guidance anywhere from Microsoft that says that is a good idea. (I do know that some think it somehow makes the return value immutable, but really anyone can cast it back to the base type and make changes to it. Or just use dynamic and never even notice you gave them an IEnumerable.)
That's the rule for return types and local variables. For parameters you should be as accepting as possible. In practice that means accepting either IEnumerable or IList depending on whether or not you need to access it by index.
AVOID using ICollection or ICollection as a parameter just to access the
Count property.
The reason for this is that if you need the Count, you probably need to access it by index as well. If not today, then tomorrow. So go ahead and use IList just in case.
(I'm not sure I agree, but it does make some sense.)
In short, how do I implement ICollection on an IEnumerable?
Short answer: the .Count() extension method. Make sure you import System.Linq.
Long answer:
int count = 0;
if (x is ICollection)
count = ((ICollection)x).Count;
else
foreach (var c in x)
count ++;
IEnumerable is an interface, and so is ICollection. It's the object's type that implements one or the other or both. You can check if an object implements ICollection with obj is ICollection.
Example:
public class MyCollection<T> : IEnumerable<T>, ICollection<T>
{
// ... Implemented methods
}
// ...
void Foo(IEnumerable<int> elements)
{
int count;
if (elements is ICollection<int>) {
count = ((ICollection<int>)elements).Count;
}
else {
// Use Linq to traverse the whole enumerable; less efficient, but correct
count = elements.Count();
}
}
// ...
MyCollection<int> myStuff;
Foo(myStuff);
Doesn't ICollection implement IEnumerable already? If you need a collection then you need a collection.

Unnecessary cast to IComparer?

I understand how to use the IComparer interface with helper classes which provide custom ways to sort. For example, here is a typical example which is very much like all the examples I have seen on the web including Microsoft's online help page:
// This helper class is used to sort an array of people by name,
// where 'Person' is a class type.
public class PeopleNameComparer : IComparer
{
// Test the name of each object.
int IComparer.Compare(object o1, object o2)
{
Person p1 = o1 as Person;
Person p2 = o2 as Person;
if (p1 != null && p2 != null)
return string.Compare(p1.Name, p2.Name);
else
throw new ArgumentException("Parameter is not a Person!");
}
}
I also understand that if we have an array of type Person (myPeople), we can then sort this array with:
Array.Sort(myPeople, new PeopleNameComparer());
In this case we are creating a new PeopleNameComparer object which is of type IComparer and passing it into the Array.Sort() method as a second parameter.
Now to make things neater, we can implement a property to provide the object user with a more friendly way to invoke the custom sort:
public static IComparer SortByName
{ get { return (IComparer)new PeopleNameComparer(); } }
What I don't understand with this kind of Property is why do all the examples use the (IComparer) cast to cast the newly created helper class(PeopleNameComparer in this example) into an IComparer object when this object is already of type IComparer? I have tried without the cast and the code appears to work fine:
// This property seems to work fine without the cast?
public static IComparer SortByName
{ get { return new PeopleNameComparer(); } }
I could understand it if the 'new' keyword returned a plain vanilla System.Object type which would then have to be casted to the appropriate IComparer, but just cannot see the need for the cast here.
But I followed Microsoft's example, and my example is similar to an example in my Pro C# book.
Is there any reason why a cast would be necessary here?
Using an explicit cast is more explicit. Pardon the truism.. but it's just that. It helps make the code more readable.
In some cases explicit casts can help the runtime disambiguate a cast if there are multiple possible options but that doesn't seem to occur with return type. Only in expressions. Following is a common example where you would need an explicit cast in an expression:
public class StringEnumerable : IEnumerable, IEnumerable<String>
{
IEnumerator<String> IEnumerable<String>.GetEnumerator()
{
yield return "TEST";
}
public IEnumerator GetEnumerator()
{
// without the explicit cast of `this` to the generic interface the
// method would call itself infinitely until a StackOverflowException occurs
return ((IEnumerable<String>)this).GetEnumerator();
}
}
If you remove the explicit cast from non-generic interface implementation it will cause an infinite loop.
The cast is redundant.
Perhaps it may have been necessary before the code was refactored from something else.
Typically you see a lot of fluff left in code over long system life-cycles where designs have changed.
You may also see other redundant constructs when language features have changed (i.e. C# automatic properties) over time.
I think redundant code decreases readability and tools like Resharper will warn you and help you remove them.
If your question is simply why the examples cast the PeopleNameComparer to IComparer, you are correct that it is not necessary at all. I would imagine it is for clarity to demonstrate to beginners that there is the implied relationship between the result and the interface.
I don't know about "all" examples but indeed the 2 variants of code should work identically. Maybe they just think that explicit casting is more readable.

Generic type casting

If i have a type and an object eg.:
- Type someType (coming from somewhere, could be any class eg. MyClass.GetType())
- Object someObject (eg. List<MyClass>())
and want to cast the object back to List<MyClass>. How should i do this?
You can't do this. Generics ensure compile-time safety. You cannot have compile time safety because you know the actual type only at runtime.
You have a runtime type and you want to perform a compile time cast. This is not possible. It is also not clear why you would want to do this in the first place. If you are interested in cases that require reflection, perhaps you should investigate that topic further.
There is no way to have compile-time typing a variable when you only receive the Type information at runtime.
This is different from generics since in generics you get the type information at compile time:
void MyFunc<T>(T thing)
{
// T is passed in at compile time
}
In your case you are getting the type at runtime. So while you can't cast the member to the type the way you normally would you can reflect on the instance and call its members:
void MyFunc(object thing, Type type)
{
var res = t.GetMethod("Add").Invoke(a, new []{"someArg"});
}
Casting means explicitly specifying the type you want to convert to. Since you don't know what your type is, you can't cast to it.
That doesn't mean you can't access the list. If you know the object you have is a list of something, you can cast it to the non-generic IList interface, which provides most of the methods and properties you need:
object obj = GetMyList();
IList list = (IList)obj;
object fifthItem = list[4];
list.RemoveAt(list.Count - 1);
If you describe the problem you're trying to solve rather than the solution you are trying to achieve, then more fitting solutions might be posted.
If you are trying to cast a runtime type at compile time, it is impossible as may said before me.
However, you could cheat a little (but don't use this technique excessively, it leads down a dark road...)
public void DoSomething<T>(List<T> object) where T : ....
{
//do what you want here
}
public void CallIt(Type t, object o) //o is List<Foo>
{
this.GetType().GetMethod("DoSomething").MakeGenericMethod(t).Invoke(o);
}
However I don't see any real benefit to this, as if you don't write any type constraint you gain nothing with using generics instead of objects and IList interface, and if you write any baseclass or interface there, you could just cast your object to that. (For example if you know that T implements IFoo, you could cast o to IList<IFoo> and have every benefit of List<Foo>...

Categories