Casting a collection's generics implicitly - c#

Last night I learned about this wonderful operation of casting by example: a very cool way to generate a collection of some Type using a reference to an existing instance.
My problem is that although this works when you explicitly create the instance, the Type of collection produced is inaccurate if you use activator to instantiate from a Type.
class TestCollectionContent
{
public int id { get; private set; }
}
[Test]
public void TestListCastCreation()
{
var explicitCast = new TestCollectionContent (); //This casts as TestCollectionContent
var explicitList = MakeList (explicitCast); //This casts as List<CommandWithExecute>
explicitList.Add (new TestCollectionContent ());
Type clazz = typeof(TestCollectionContent);
var implicitCast = Activator.CreateInstance (clazz);//This casts as TestCollectionContent
var implicitList = MakeList (implicitCast); //This casts as List<object>
implicitList.Add (new TestCollectionContent ());
Assert.AreEqual (explicitCast.GetType (), implicitCast.GetType ()); //Succeeds
Assert.AreEqual (explicitList.GetType (), implicitList.GetType ()); //FAILS!
}
public static List<T> MakeList<T>(T itemOftype)
{
List<T> newList = new List<T>();
return newList;
}
For my purpose it is imperative that the collection be correctly cast. Any thoughts?
Note that I'm using C# with Unity3D (which uses something akin to .Net 3.5).

Activator.CreateInstance always returns an object, so you will not get any static type information from it when using it. This will make the variable implicitCast of type object although its value is of a more specialized type.
Now when using generics, only the type that is available for static typing is taken into account. So when passing implicitCast to MakeList, all that method sees is an object. As such, the method will be called as MakeList<object> and will return a List<object>, which is of course not of the same type as explicitList.
Unfortunately (or fortunately?) you cannot really do this any better. Generics are supposed to be something for use in a static typing environment, and if you start to create types dynamically, you will lose this ability.
You could however use Activator.CreateInstance for the list creation just as well by doing something like this:
public static IList MakeList(object itemOftype)
{
Type listType = typeof(List<>).MakeGenericType(itemOfType.GetType());
return (IList) Activator.CreateInstance(listType);
}
Of course, this will also just return an object, so you will have to cast it to a more specialized type, or use the non-generic IList interface to have at least some access to it.

This code behaves this way because T is inferred at compile time, not run time. Since implicitCast is of type object, it compiles with MakeList<object>.
var implicitList = MakeList (implicitCast); // equivalent to
List<object> implicitList = MakeList<object>(implicitCast);
var explicitList = MakeList (explicitCast); // equivalent to
List<TestCollectionContent> explicitList =
MakeList<TestCollectionContent>(explicitCast);
If you want it to use the runtime type, you can use reflection or dynamic.

Related

is it necessary to use MakeGenericType method?

I saw code like this:
internal sealed class MyDictionary<TKey, TValue> { }
public static class Program {
public static void Main() {
// Get a reference to the generic type's type object
Type openType = typeof(MyDictionary<,>);
// Close the generic type by using TKey=String, TValue=Int32
Type closedType = openType.MakeGenericType(typeof(string), typeof(int));
// Construct an instance of the closed type
Object o = Activator.CreateInstance(closedType);
Console.WriteLine(o.GetType());
}
}
But why we need to use the MakeGenericType method, why not just do:
...
Type concreteType= typeof(MyDictionary<string, int>);
Object o = Activator.CreateInstance(concreteType);
Console.WriteLine(o.GetType());
the results are the same.
It looks like MakeGenericType method add extra unnecessity, so does MakeGenericType provide useful features that I overlook?
MakeGenericType is for when you don't know the type at compile time. Therefore you are correct - you wouldn't really use the code you saw, as the types were known at compile time, so you may as well write new MyDictionary<string, int>().
However, the code could rewritten like this:
internal sealed class MyDictionary<TKey, TValue> { }
public static class Factory {
public static MyDictionary<TKey, TValue> Create<TKey, TValue>() {
// Get a reference to the generic type's type object
Type openType = typeof(MyDictionary<,>);
// Close the generic type by using type constraints
Type closedType = openType.MakeGenericType(typeof(TKey), typeof(TValue));
// Construct an instance of the closed type
Object o = Activator.CreateInstance(closedType);
return (MyDictionary<TKey, TValue>)o;
}
}
These two would effectively create the same object:
var dictionary0 = new MyDictionary<string, int>();
var dictionary1 = Factory.Create<string, int>();
Console.WriteLine(dictionary0.GetType() == dictionary1.GetType());
// true
Even here it's not that useful because the return type requires generic constraints. Where I use it is for situations that require an interface or abstract return type, but you need to create a generic implementation, for example:
internal sealed class MyDictionary<TKey, TValue> : ISomeFeature { }
public static class Factory {
public static ISomeFeature Create(Type key, Type value) {
// Get a reference to the generic type's type object
Type openType = typeof(MyDictionary<,>);
// Close the generic type by using arguments
Type closedType = openType.MakeGenericType(key, value);
// Construct an instance of the closed type
Object o = Activator.CreateInstance(closedType);
return (ISomeFeature)o;
}
}
Now suddenly it becomes a whole lot more useful. Because it relies on Type objects instead of generic constraints, you can pass it types dynamically - i.e. at run time.
Short answer: NO! it is not necessary to use makeGenericType, but sometimes it's a necessity
Generics are best used when you know the types at compile time. In fact there is a good argument that if you don't know the types at compile time, you probably shouldnt be using generics!
That said, there are often times when you need to use a generic implementation (perhaps a 3rd party library) and you only know the types at runtime... that is where Reflection, and specifically MakeGenericType comes in.
You code was no doubt an example of how to do this, because actually that code is only doing
var dict = new MyDictionary<string,int>();
Console.WriteLine(dict.GetType());
Assuming the types were known at compile time.

Internal class masked by object

Assume that class (B)'s public function has the return line:
return (object)(new List<A>{Some elements})
where A is an internal and sealed class. I cannot change the code of A or B.
After I call this function in B, how do I find the first element of that list. C# does not let me cast that list back into List<A> because A is internal.
Just because you can read the source code or disassemble the code, you should not rely on the current implementation, rather try to use the public interface.
List<A> implements the non-generic IList, so you can cast back to IEnumerable or IList if you really look for trouble.
You can cast a generic List to the non-generic IEnumerable, iterate over that, and then use Object.ToString() to get information about the B instances, or you can just return the reference.
Object obj = new List<string> () { "dd", "ee" };
IEnumerable enumerable = obj as IEnumerable;
bool foundSomething = false;
foreach (var thing in enumerable)
{
if(!foundSomething)
{
// Console.Write(thing.ToString()); If you want
foundSomething = true;
return thing;
}
}
Perhaps I'm misunderstanding the question here, but if A is sealed, you can still write an extension method to iterate or handle the list.
Extension methods for sealed class in c#
You can use interface covariance to cast to IEnumerable<object> and then use some of LINQ's extension methods:
var aItems = (IEnumerable<object>) B.Foo();
Console.WriteLine(aItems.First());
To get first element without touching anything you can do this:
object result = b.MethodThatReturnsList();
object firstEl = ((IList)result)[0];
Problem is that firstElvariable can only be object and you can't cast it to A because it is not accessible. Not very helpful though.
Here is the real problem: you can't declare public methods that return some private/internal types. You will get this compilation error.
Solution is to design a public interface that A will implement and return List<IYourInterface>. Another option is to have public base class.

How to cast an IEnumerable<object> to an IEnumerable<runtime type>

I'm trying to accomplish the following.
Suppose I have this data model:
public class Article
{
public ICollection<string> Tags { get; set; }
}
These tags are retrieved from a database. My database's API returns them to me as a List<object>.
Therefore, I need to make a conversion from List<object> to something that implements ICollection<string>.
I am aware of the LINQ Cast<T>() method that cast its elements to the given type and returns the converted IEnumerable<T>.
However, I cannot use Cast<string>() because that would always cast my List<object> to IEnumerable<string>, not giving any options for models that have ICollection<double> properties (or any other type).
I can use reflection and get the generic type parameter:
Type genericArg = collectionType.GetGenericArguments().First();
But that would leave me with a runtime Type, which I cannot use as Cast<genericArg>().
How can I cast an IEnumerable<object> to an IEnumerable of a dynamic Type?.
I should note that no complex types are allowed on my model, so anything like:
public ICollection<Tag> Tags { get; set; }
will not happen. I only handle primitive types.
You have a basic misunderstanding about casting.
The result type of a casting operation must be known at compile time.¹
Consider the following example:
string a = "abc";
object b = (object)a;
string c = (string)b;
The runtime type of a, b and c is the same. It's string. The compile-time type is different. Casting is only relevant for the compile-time type.
Thus, the answer to your question
How to cast an IEnumerable<object> to an IEnumerable<runtime type>
is: You don't. Casting does not make sense for runtime types.
That said, let me offer a solution to your real problem: Let's say you have an IEnumerable<object> values, a Type myTargetType and want to create a List<typeof(myTargetType)> containing the values.
First, you create the list using reflection:
var listType = typeof(List<>).MakeGenericType(myTargetType);
IList myList = (IList)Activator.CreateInstance(listType);
And then you fill the list:
foreach (var item in values)
{
myList.Add(item);
}
Obviously, Add will throw an ArgumentException if an entry of values is not of runtime type myTargetType.
¹ The result type can be a generic type, but generic type parameters have to be specified at compile time as well.
I believe System.Convert has what you need:
Type genericArg = collectionType.GetGenericArguments().First();
foreach(var obj in collection) {
yield return Convert.ChangeType(obj, genericArg);
}
Enumerable.Cast<T>(this IEnumerable source) is normally what you'd be looking for. It is possible to use reflection to close the generic type yourself if different variations are required:
class Program
{
static void Main(string[] args)
{
var source = new List<object> {
"foo",
"bar",
"baz"
};
var type = typeof(string); // or however you find out the type
var castMethod = typeof(Enumerable)
.GetMethod("Cast").MakeGenericMethod(
new[] {
type
});
var result = (IEnumerable<string>)
castMethod.Invoke(null, new object[] {source});
foreach (var str in result)
{
Console.WriteLine(str.ToUpper());
}
}
}
The other problem is that it is not meaningful to cast from one List<T> to another - the generic parameter is invariant, because the collection is read-write. (Arrays allow some such casting for historical reasons.) If you're only reading, though, the IEnumerable<T> returned from Cast is sufficient.
You need to implement a generic method which take result from your database api and return appropriate collection as per your model, something like below:
private ICollection<T> RetrieveTags()
{
// Get tags using database api
return tags.Cast<T>();
}
Then call this method to get model as needed, for example:
ICollection<int> t1 = RetrieveTags<int>();
ICollection<string> t2 = RetrieveTags<string>();

How to know in C# code which type a variable was declared with

I want to have some function that would return "Base" if a variable of class Base was passed to it, "Derived" if it was declared as Derived, etc. Not depending on runtime type of a value it was assigned to.
See code below for example. The key is to use Generics, extension method was used just for nice syntax.
using System;
static class Program
{
public static Type GetDeclaredType<T>(this T obj)
{
return typeof(T);
}
// Demonstrate how GetDeclaredType works
static void Main(string[] args)
{
ICollection iCollection = new List<string>();
IEnumerable iEnumerable = new List<string>();
IList<string> iList = new List<string>();
List<string> list = null;
Type[] types = new Type[]{
iCollection.GetDeclaredType(),
iEnumerable.GetDeclaredType(),
iList.GetDeclaredType(),
list.GetDeclaredType()
};
foreach (Type t in types)
Console.WriteLine(t.Name);
}
}
Result:
ICollection
IEnumerable
IList`1
List`1
EDIT:
You may also avoid using extension method here, as it would cause it to appear on every IntelliSense drop-down list. See another example:
using System;
using System.Collections;
static class Program
{
public static Type GetDeclaredType<T>(T obj)
{
return typeof(T);
}
static void Main(string[] args)
{
ICollection iCollection = new List<string>();
IEnumerable iEnumerable = new List<string>();
Type[] types = new Type[]{
GetDeclaredType(iCollection),
GetDeclaredType(iEnumerable)
};
foreach (Type t in types)
Console.WriteLine(t.Name);
}
}
also produces correct results.
This is not possible without parsing the code in question.
At runtime only two pieces of type information are available, the actual type of a value (via object.GetType()) and, if the variable in question is a parameter or class/instance variable the FieldType property on a FieldInfo, PropertyType on a PropertyInfo or ParameterType on a ParameterInfo.
Since the value passed to you may well have come via several variables on its route to you the question is not even well defined I'm afraid.
Ah - I see you want only the type as currently defined in the method, the Expression functionality will provide this (Roman's answer shows a neat way to do this) but beware trying to use this outside the method... In essence you are letting the generic type inference of the compiler infer the type in question but this means that the variable used is not always the variable you can see. It may instead be that of a compiler synthesised variable, for example:
string x = "x";
Console.WriteLine(x.GetDeclaredType()); // string
Console.WriteLine(((object)x).GetDeclaredType()); // object
Since the compiler synthesises a temporary variable in which to place an object reference to x.
Just recurse on GetType() until you hit object.

Initialize generic object with unknown type

How can I initialize a list containing generic objects whose types can be different?
For example, I have the following:
this.Wheres = new List<Where<>>();
As you know, <> is not valid syntax. However, sometimes the type passed to Where will be a string and sometimes it will be DateTime, etc. I tried using object as the initialized type, but that doesn't work either.
Well, you haven't really given enough context (what's SqlWhere?) but normally you'd use a type parameter:
public class Foo<T>
{
private IList<T> wheres;
public Foo()
{
wheres = new List<T>();
}
}
If you want a single collection to contain multiple unrelated types of values, however, you will have to use List<object>
this.Wheres = new List<Object>();

Categories