parametros - tipos genericos en c#
Cómo detectar si el tipo es otro tipo genérico (9)
Desea verificar el método Type.IsInstanceOfType
ejemplo:
public static void DoSomething<K,V>(IDictionary<K,V> items) {
items.Keys.Each(key => {
if (items[key] **is IEnumerable<?>**) { /* do something */ }
else { /* do something else */ }
}
¿Se puede hacer esto sin usar el reflejo? ¿Cómo digo IEnumerable en C #? ¿Debería usar IEnumerable ya que IEnumerable <> implementa IEnumerable?
Una palabra de advertencia sobre los tipos genéricos y el uso de IsAssignableFrom () ...
Digamos que tienes lo siguiente:
public class MyListBase<T> : IEnumerable<T> where T : ItemBase
{
}
public class MyItem : ItemBase
{
}
public class MyDerivedList : MyListBase<MyItem>
{
}
Llamar a IsAssignableFrom en el tipo de lista base o en el tipo de lista derivada devolverá falso, pero claramente MyDerivedList
hereda MyListBase<T>
. (Una nota rápida para Jeff, los genéricos deben estar envueltos en un bloque de código o tildes para obtener la <T>
, de lo contrario se omite. ¿Esto es así?) El problema se debe al hecho de que MyListBase<MyItem>
se trata como un tipo diferente de MyListBase<T>
. El siguiente artículo podría explicar esto un poco mejor. http://mikehadlow.blogspot.com/2006/08/reflecting-generics.html
En cambio, pruebe la siguiente función recursiva:
public static bool IsDerivedFromGenericType(Type givenType, Type genericType)
{
Type baseType = givenType.BaseType;
if (baseType == null) return false;
if (baseType.IsGenericType)
{
if (baseType.GetGenericTypeDefinition() == genericType) return true;
}
return IsDerivedFromGenericType(baseType, genericType);
}
/ EDITAR: la nueva publicación de Konrad, que tiene en cuenta la recursividad genérica y las interfaces, es perfecta. Muy buen trabajo. :)
/ EDIT2: si se realiza una comprobación sobre si genericType es una interfaz, se podrían obtener beneficios de rendimiento. El control puede ser un bloque si alrededor del código de interfaz actual, pero si está interesado en usar .NET 3.5, un amigo mío ofrece lo siguiente:
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaces = givenType.GetInterfaces().Where(it => it.IsGenericType).Select(it => it.GetGenericTypeDefinition());
var foundInterface = interfaces.FirstOrDefault(it => it == genericType);
if (foundInterface != null) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType ?
baseType.GetGenericTypeDefinition() == genericType :
IsAssignableToGenericType(baseType, genericType);
}
Usaría la sobrecarga:
public static void DoSomething<K,V>(IDictionary<K,V> items)
where V : IEnumerable
{
items.Keys.Each(key => { /* do something */ });
}
public static void DoSomething<K,V>(IDictionary<K,V> items)
{
items.Keys.Each(key => { /* do something else */ });
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) {
Gracias por la gran información. Por conveniencia, he refactorizado esto en un método de extensión y lo redujo a una sola declaración.
public static bool IsAssignableToGenericType(this Type givenType, Type genericType)
{
return givenType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType) ||
givenType.BaseType != null && (givenType.BaseType.IsGenericType && givenType.BaseType.GetGenericTypeDefinition() == genericType ||
givenType.BaseType.IsAssignableToGenericType(genericType));
}
Ahora se puede llamar fácilmente con:
tipo.IsAssignableToGenericType (typeof (MyGenericType <>))
Muchas gracias por esta publicación. Quería proporcionar una versión de la solución de Konrad Rudolph que me haya funcionado mejor. Tuve problemas menores con esa versión, especialmente cuando comprobé si un tipo es un tipo de valor que admite valores de nulo:
public static bool IsAssignableToGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
No estoy seguro de entender lo que quieres decir aquí. ¿Desea saber si el objeto es de algún tipo genérico o desea probar si se trata de un tipo genérico específico ? ¿O solo quieres saber si es enumerable?
No creo que el primero sea posible. El segundo es definitivamente posible, solo tráelo como cualquier otro tipo. Para el tercero, simplemente pruébelo contra IEnumerable como sugirió.
Además, no puede usar el operador ''es'' en los tipos.
// Not allowed
if (string is Object)
Foo();
// You have to use
if (typeof(object).IsAssignableFrom(typeof(string))
Foo();
Consulte esta pregunta sobre los tipos para obtener más detalles. Tal vez te ayude.
Solía pensar que tal situación puede resolverse de una manera similar a la solución de @Thomas Danecker, pero agregando otro argumento de plantilla:
public static void DoSomething<K, V, U>(IDictionary<K,V> items)
where V : IEnumerable<U> { /* do something */ }
public static void DoSomething<K, V>(IDictionary<K,V> items)
{ /* do something else */ }
Pero ahora noté que no funciona a menos que especifique los argumentos de la plantilla del primer método explícitamente. Esto claramente no se personaliza para cada artículo en el diccionario, pero puede ser una especie de solución de pobre.
Estaría muy agradecido si alguien pudiera señalar algo incorrecto que podría haber hecho aquí.
La respuesta previamente aceptada es agradable, pero está mal. Afortunadamente, el error es pequeño. La comprobación de IEnumerable
no es suficiente si realmente desea conocer la versión genérica de la interfaz; hay muchas clases que implementan solo la interfaz no genérica. Daré la respuesta en un minuto. Primero, sin embargo, me gustaría señalar que la respuesta aceptada es demasiado complicada, ya que el siguiente código lograría lo mismo en las circunstancias dadas:
if (items[key] is IEnumerable)
Esto hace aún más porque funciona para cada elemento por separado (y no en su subclase común, V
).
Ahora, para la solución correcta. Esto es un poco más complicado porque tenemos que tomar el tipo genérico IEnumerable`1
(es decir, el tipo IEnumerable<>
con un parámetro de tipo) e inyectar el argumento genérico correcto:
static bool IsGenericEnumerable(Type t) {
var genArgs = t.GetGenericArguments();
if (genArgs.Length == 1 &&
typeof(IEnumerable<>).MakeGenericType(genArgs).IsAssignableFrom(t))
return true;
else
return t.BaseType != null && IsGenericEnumerable(t.BaseType);
}
Puede probar la corrección de este código fácilmente:
var xs = new List<string>();
var ys = new System.Collections.ArrayList();
Console.WriteLine(IsGenericEnumerable(xs.GetType()));
Console.WriteLine(IsGenericEnumerable(ys.GetType()));
rendimientos:
True
False
No se preocupe demasiado por el hecho de que esto utiliza la reflexión. Si bien es cierto que esto agrega sobrecarga de tiempo de ejecución, también lo hace el uso del operador is
.
Por supuesto, el código anterior está muy restringido y podría expandirse a un método más general, IsAssignableToGenericType
. La siguiente implementación es ligeramente incorrecta 1 y la dejaré aquí solo con fines históricos . No lo uses En cambio, James ha proporcionado una implementación excelente y correcta en su respuesta.
public static bool IsAssignableToGenericType(Type givenType, Type genericType) {
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
if (it.IsGenericType)
if (it.GetGenericTypeDefinition() == genericType) return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == genericType ||
IsAssignableToGenericType(baseType, genericType);
}
1 Falla cuando genericType
es el mismo que givenType
; por la misma razón, falla para los tipos que aceptan nulos, es decir,
IsAssignableToGenericType(typeof(List<int>), typeof(List<>)) == false
IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false
Creé una esencia con un conjunto completo de casos de prueba .