I have a function which needs to go through a class' types and identify a specific type. If I find that type, I want to cast it to the type that I know it is. But in the code below, the following case is failing:
BaseTableObjectList<BaseTableObject> obj = pi.GetValue(item, null) as BaseTableObjectList<BaseTableObject>;
Here is the code. Basically, I am making an iterator which returns certain properties. Why can't I cast?
foreach (PropertyInfo pi in item.GetType().GetProperties())
{
if (pi.PropertyType.BaseType == null) continue; // skip interfaces
if (!pi.PropertyType.BaseType.IsGenericType) continue; // only interested in childlists
Type[] types = pi.PropertyType.BaseType.GetGenericArguments();
// is my generic argument derived from baseObject?
if (typeof(BaseTableObject).IsAssignableFrom(types[0]))
{
// this is a child we want
log.Info(types[0].Name);
BaseTableObjectList<BaseTableObject> obj = pi.GetValue(item, null) as BaseTableObjectList<BaseTableObject>;
yield return obj;
}
}
Make sure that pi.GetValue(item, null).GetType() is actually the type you want to cast to. It seems like it returns BaseTableObject instead BaseTableObjectList<BaseTableObject>
If your class Company : BaseTableObject and your CompanyList : BaseTableObjectList<Company> then your result can be cast to BaseTableObjectList<BaseTableObject> by using CompanyList.Cast<BaseTableObject>()
Edit2: From your comments, I think the following code will do the work.
BaseTableObjectList<BaseTableObject> obj = new BaseTableObjectList<BaseTableObject>((pi.GetValue(item, null) as System.Collections.IEnumerable).Cast<BaseTableObject>());
The problem you have I think is one of covariance, which only applies to interfaces. If you had a IBaseTableObjectList<out T> where T : BaseTableObject the cast wouldn't fail. Covariance for generics is only supported on delegates and interfaces not on base classes:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
In C#, variance is supported in the following scenarios:
Covariance in arrays (since C# 1.0)
Covariance and contravariance in delegates, also known as “method group variance” (since C# 2.0)
Variance for generic type parameters in interfaces and delegates (since C# 4.0)
This is essentially the same problem is assigning a List<string> to an IEnumerable<object> which is one of the examples in the linked article.
Related
I know this question was here multiple times but i did some searching and i couldn't find solution that would fit:
private void UpdateCollections<T>(ICollection<T> updatedFormulas,
ICollection<T> currentFormulas) where T: IIdentifiable
{
// Irrelevant code omitted
foreach (var prop in newOrUpdatedFormula.GetType().GetProperties())
{
if (prop.PropertyType != typeof(string) &&
prop.PropertyType.GetInterface(typeof(IEnumerable).Name) != null &&
prop.GetValue(newOrUpdatedFormula) != null)
{
// the dynamic type i need is stored here =>
// prop.PropertyType.GenericTypeArguments[0]
UpdateCollections((ICollection<??>prop.GetValue(newOrUpdatedFormula),
(ICollection<??>prop.GetValue(existingFormula));
}
}
}
I need to go through two collections of objects to compare them. inside of those two collections there are other sub-collections of different type (but still implementing IIdentifiable interface). It goes this way few levels deep. So the '??' type i know at runtime and i know it implements IIdentifiable.
I've tried casting to ICollection<IIdentifiable> and it didn't work, i've tried to play with MakeGenericType() method but also it didn't help.
Something like below won't even compile:
Type typeInCollection = typeof(ICollection<>)
.MakeGenericType(prop.PropertyType.GenericTypeArguments[0])
UpdateCollections(Convert.ChangeType(prop.GetValue(newOrUpdatedFormula),
typeInCollection), Convert.ChangeType(prop.GetValue(existingFormula), typeInCollection));
What's the easiest way to make this recursive method work when i know the type inside ICollection at runtime?
I have two interfaces
public interface ISerializableDictionary { ... }
public interface ISerializableDictionary<TKey,TValue>
: ISerializableDictionary { ... }
I need to cast from the former to the latter at run time using reflection.
It's clearly easy to interrogate the former with GetType().GetGenericArguments.
But how do I then do the cast? I have this code below but it is failing to compile, for the obvious reason that I am trying to use a variable as a type.
Type[] genericTypes = dictionary.GetType().GenericTypeArguments;
Type keyType = genericTypes[0];
Type valueType = genericTypes[1];
// this compiles but doesn't do the cast
Type dictType = typeof(SerializableDictionary<,>).MakeGenericType(keyType, valueType);
var createdDictionary = Activator.CreateInstance(dictType);
// this is the line that won't compile - 'dictionary' is a SerializableDictionary, and I want to access it through its typed generic interface
ISerializableDictionary<keyType,valueType> serializableDictionary = dictionary as ISerializableDictionary<keyType, valueType>;
The more specified interface has a method which I need to call. The less specified interface does not (and can't ever be, because the call needs a typed argument).
Is the solution something to do with dictionary.GetType().GetInterfaces()?
Any steer will be wildly appreciated. Programming solo at the moment so I don't have a team to call on, hence the query here.
UPDATE - in response to comments
The problem I am trying to solve is how to serialize members of an object where the members are themselves enumerable. I am trying to figure out how serialization libraries do it as a learning exercise and because I have a few ideas that I want to explore. Serialization & Reflection are not my main areas of programming so I am stumbling to learn them.
So I have (as reduced code):
public class ExperimentalSerializer<T>
{
public void Serialize(T objectToSerialize)
{
IEnumerable<object> collectionsToSerializeToCSV = objectToSerialize.GetEnumerableMembers();
foreach (object collectionToSerialize in collectionsToSerializeToCSV)
{
string csvString = "";
if (collectionToSerialize.IsDictionary())
{
// serialize dictionary here to csvString
// but cannot properly access contents through just IDictionary
// need IDictionary<TKey,TValue>
// ** ALSO SEE TEXT BELOW THIS CODE SNIPPET**
}
else if (collectionToSerialize.IsList())
{
// serialize list here to csvString
}
else if (collectionToSerialize.GetType().IsArray)
{
// serialize array here to csvString
}
// save csvString to somewhere useful here
}
}
}
And elsewhere I have an extension method:
public static IEnumerable<object> GetEnumerableMembers(this object objectToInterrogate)
{
Type objectType = objectToInterrogate.GetType();
// get the enumerable properties
PropertyInfo[] properties = objectType.GetProperties(BindingFlags.Instance | BindingFlags.Public);
IEnumerable<PropertyInfo> enumerableProperties = properties.Where(propertInfo => propertInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEnumerable)));
IEnumerable<PropertyInfo> serializableProperties = enumerableProperties.Where(p => p.IsSerializable());
IEnumerable<object> enumerablePropertyValues = serializableProperties.Select(p => p.GetValue(objectToInterrogate, null));
// get the enumerable fields
FieldInfo[] fields = objectType.GetFields(BindingFlags.Instance | BindingFlags.Public);
IEnumerable<FieldInfo> enumerablefields = fields.Where(propertInfo => propertInfo.FieldType.GetInterfaces().Any(x => x == typeof(IEnumerable)));
IEnumerable<object> enumerablefieldValues = enumerablefields.Select(f => f.GetValue(objectToInterrogate));
// merge the two lists together
IEnumerable<object> enumerableMembers = enumerablePropertyValues.Union(enumerablefieldValues);
return enumerableMembers.ToList();
}
One specific challenge I am investigating is how to serialize an enumerable (Dictionary, List or array TValue[]) where TValue is itself a complex type (e.g. a class that can be serialized). This cannot be ascertained without knowing the type of TValue, but this cannot be retrieved from IDictionary or IList alone and these can only be enumerated with the type object.
This is the very specific point I am trying to investigate and potentially to control: how to determine TValue and then to work out if/how to serialize it in turn. My idea is to cast to more-specified generics with known type parameters but I get a bit lost at this point.
Hope this helps.
#SLaks points out in the comments:
Casting is inherently a compile-time operation. Casting to a type only known at runtime makes no sense. You can't call your method if its types are not known at compile-time.
That's absolutely right. You can, of course, still call the intended method at runtime, but you'll need to use (more) reflection to do it, since you have no way to get the compiler to generate a statically-typed call.
To do this, take the Type object you already constructed using MakeGenericType(), and call GetMethod() on it to get the Type.MethodInfo object corresponding to the method to call. Then, call MethodInfo.Invoke().
Type dictType = typeof(SerializableDictionary<,>).MakeGenericType(keyType, valueType);
MethodInfo method = dictType.GetMethod("MyMethod");
object returnValue = method.Invoke(dictionary, new object[] { /* arguments */ });
TMI...
When you write dictionary.MyMethod(), the C# compiler generates a Callvirt IL (byte code) instruction. The object to call the method on (and the arguments to the method) are pushed onto the stack, and the argument to Callvirt is the metadata token corresponding to the type-qualified ISerializableDictionary<TKey,TValue>.MyMethod method. This is the normal calling mechanism in .NET. When you (and the compiler) don't know what TKey and TValue are at compile time, there's no way to get the right metadata token for the method, and no way to generate the Callvirt. That's why you have to use the reflection API.
You can, however, use something like DynamicMethod to generate your own IL and JIT it at runtime. Once JITted, the call is just as fast as one statically generated by the compiler. There is of course significant overhead to generating a dynamic method, but it's a one-time overhead.
Of course, #DavidL points out:
The approach here seems wildly off-course. Instead of asking for a specific solution, can you please describe the specific concrete problem that you are trying to solve?
That, too, is absolutely right. So don't do what I just suggested unless you really, really know what you're doing and have a really, really good reason. (Hint: You don't.) But I thought this information might give you a better overall picture of why you can't do what you expected to do.
I am trying to make a function that generically retrieves data from my MongoDB collections. To do this I have constructed a generic method that returns a List<T>.
My issue is that I have to create this List<T> to return, but I do so based on the typeof T. I am not sure what I need to do to please the compiler..
public async Task<List<T>> GetDocsAsync<T>(
CollectionTypes collection, // Enum representing my Collections
FilterDefinition<BsonDocument> search,
SortDefinition<BsonDocument> sort = null)
{
// Get BsonDocuments from the collection based on the search and sort criteria
List<BsonDocument> matchedDocs;
IMongoCollection<BsonDocument> MongoCollection = GetCollection(collection);
if (sort == null) matchedDocs = await MongoCollection.Find(search).ToListAsync();
else matchedDocs = await MongoCollection.Find(search).Sort(sort).ToListAsync();
// Return a List<T>, covert matchedDocs to List<T> if need be
Type docType = typeof(T);
if (docType == typeof(BsonDocument))
return matchedDocs;
else if (docType == typeof(LogEvent_DBDoc))
return LogEvent_DBDoc.ConvertFromBson(matchedDocs);
// ...
}
At both of the return lines I receive an error along the lines of "Cannot implicitly convert from List<[KnownType]> to List<T>. Which makes sense to me, because the typeof T does not necessarily match the typeof say BsonDocument. But I have made the proper check to do so.
Can I cast List<[KnownType]> to List<T>?
You are abusing generic syntax. Generic code should be generic, i.e. work with whatever type you use.
You should have different methods, depending on the type that will be passed in. By all means, make the truly generic parts into its own generic method, which your type-specific methods can call. But have the caller, who already knows what type it's using, pick the appropriate method based on that type, and then just use that type explicitly in each type-specific method.
It's hard to say with the example you have, but if you can provide a good Minimal, Complete, and Verifiable example that shows clearly what you're doing, I would be happy to refactor it to show what I mean.
If you're sure you have the List<KnownType> that matches the type of current generic instantiation of List<T> you can cast it to the required generic type with the help of intermediate cast to object:
List<T> GetListOf<T>() {
if (typeof(T) == typeof(String)) {
var stringList = new List<String> { "a", "b" };
return (List<T>)(object)stringList;
}
throw new NotSupportedException();
}
Leaving the moral judgement whether you should do so to yourself )
I am currently trying to get a very specific set of methods but am failing to do so.
I need to get all methods that match a certain signature from all classes that implement a certain interface.
What I've got so far is:
IEnumerable<System.Type> classes = Assembly.GetAssembly(typeof(IActionMethod)).GetTypes().Where(x => x.GetInterface("IActionMethod") != null);
MethodInfo[] methods;
List<MethodInfo> relevant;
ParameterInfo[] parameters;
foreach(System.Type cls in classes)
{
methods = cls.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
relevant.Clear();
for(int i = 0; i < methods.Length; i++)
{
parameters = methods[i].GetParameters();
if(parameters.Length == 1 && parameters[0].GetType() == typeof(GameObject) && methods[i].ReturnType == typeof(void))
relevant.Add(methods[i]);
}
}
This code already fails at GetMethods(..)which is not returning any methods.
What I do not understand is, that I am able to receive all public methods of any of the relevant classes if they do not implement the interface.
The interface itself does not contain anything, I am only using it to "mark" the relevant classes, as I could not come up with any other solution to do so.
Can anyone tell me why the interface is rendering GetMethodsuseless or point me to the error in my code above?
I suspect this is the problem:
foreach(System.Type cls in classes)
{
methods = cls.GetType().GetMethods(...)
cls is already a Type, so calling GetType() on it will return System.Type (or a subclass). I suspect you just want:
foreach(System.Type cls in classes)
{
methods = cls.GetMethods(...)
It's also unclear why you're clearing the relevant list on each iteration. It means the only entries at the end will be the ones from the last class you look at - are you sure that's what you want?
As an aside, Microsoft recommends not using 'marker interfaces' and instead suggests using attributes. You can then use Memberinfo.IsDefined() instead of checking for the interface.
To find all classes that implements specific interface, you should use rather IsAssignableFrom because your code will omit classed that implement your interface indirectly:
Type interfaceType = typeof(IActionMethod);
Assembly assembly = Assembly.GetAssembly(interfaceType );
IEnumerable<System.Type> classes = assembly.GetTypes().Where(x => interfaceType.IsAssignableFrom(x));
Then, you probably want to call:
methods = cls.GetMethods(...)
because cls is your desired type, you are currently searching in its Type.
Given
int foo = 1;
Type unboundType = typeof(List<>);
Type w = unboundType.MakeGenericType(typeof(int));
if (w == typeof(List<int>))
{
Console.WriteLine("Yes its a List<int>");
try
{
((List<int>)(object)w).Add(foo);
}
catch(InvalidCastException)
{
Console.WriteLine("No you can't cast Type");
}
}
I can verify that the type indeed matches a constructed type and perform an action based on said constructed type. However, I cannot cast Type to it's class using as or an explicit cast. Is there a practical purpose for allowing developers to create a Type of unbound type or does this functionality exist solely to support the language in some way?
Not everything can be done at compile time. Sometimes, particularly in library code, you need to take what you are given. In scenarios where you are given just an object or a Type and need to do some clever processing, unbound types can be really helpful; for example:
object obj = NoIdeaWhatThisReturns();
IList list = (IList)Activator.CreateInstance(
typeof(List<>).MakeGenericType(obj.GetType()));
list.Add(obj);
Basically; scenarios that use a lot of reflection or meta-programming will probably find themselves using unbound types at some point.
In the code you posted, you didn't actually instantiate an object of that type anywhere. You were simply trying to cast an instance of System.Type to List<int> which doesn't make sense. If you update your code to actually create an instance, it works:
int foo = 1;
Type unboundType = typeof(List<>);
Type w = unboundType.MakeGenericType(typeof(int));
if (w == typeof(List<int>))
{
Console.WriteLine("Yes its a List<int>");
object obj = Activator.CreateInstance(w);
try
{
((List<int>)obj).Add(foo);
Console.WriteLine("Success!");
}
catch(InvalidCastException)
{
Console.WriteLine("No you can't cast Type");
}
}
Maybe I'm just missing the crux of your question. Certainly depending on your logic, you could have if/else checks based on some type you don't know at compile time (in your example, you know you're working with int, but perhaps at runtime that could be other types as desired)
EDIT: Just to provide an example of a truly runtime usage, consider the following:
public object CreateList(Type elementType, object initialValue)
{
if (!elementType.IsAssignableFrom(initialValue.GetType()))
throw new ArgumentException("Incompatible types!");
Type unboundType = typeof(List<>);
Type listType = unboundType.MakeGenericType(elementType);
object list = Activator.CreateInstance(listType);
var addMethod = listType.GetMethod("Add");
addMethod.Invoke(list, new []{initialValue});
return list;
}
This lets us create a List<T> out of some unknown type/object at runtime. Some usage:
object intList = CreateList(typeof(int), 1);
object stringList = CreateList(typeof(string), "asdf");
object objectFromSomewhere = GetMyUnknownObject();
object someUnknownListType = CreateList(objectFromSomewhere.GetType(), objectFromSomewhere);
So, you might not be able to do much with the objects as are; probably could have treated them as IEnumerable at least. But that's up to what your system needs to do.
EDIT: Forgot about the IList interface:
public IList CreateList(Type elementType, object initialValue)
{
if (!elementType.IsAssignableFrom(initialValue.GetType()))
throw new ArgumentException("Incompatible types!");
Type unboundType = typeof(List<>);
Type listType = unboundType.MakeGenericType(elementType);
IList list = (IList)Activator.CreateInstance(listType);
list.Add(initialValue);
return list;
}
Type is not a placeholder for a given type - it's a specific type itself to describe other types. There's no point in trying cast it to a different, unrelated type because metadata (obviously) cannot act for that specific type.
The metadata types are used to inspect the various aspects of specific types, not to create one. If you want to create instances of types in a generic way, you can use the Activator class for that.
Type oType = ...; // get a Type instance here about a type
object[] oArgs = { oParam1, oParam2 }; // constructor parameters (if any)
return ( Activator.CreateInstance ( oType, oArgs ) );
This gives you the ability to create types based on strings, for example. You can get a Type instance for System.String (or from a function call like GetTypeNameFromUser(...)) and then create an instance of that type. (Activator has direct support for just taking a string but internally, it uses Type to look up the type that needs to be instantiated.)
Since all types are equal, you can create a Type instance for an unbound generic type just like any other type - at the very least, it allows you to inspect its properties and methods. (As the accepted answer shows, you can also use the Type instance to create specialized generic types using MakeGenericType.)