2009-07-08 6 views
124

¿Hay alguna forma de obtener la siguiente declaración de función?¿Cómo puedo usar la interfaz como una restricción de tipo genérico C#?

public bool Foo<T>() where T : interface; 

ie. donde T es un tipo de interfaz (similar a where T : class y struct).

Actualmente he conformado con:

public bool Foo<T>() where T : IBase; 

Dónde IBase se define como una interfaz de vacío que se hereda por todos mis interfaces personalizadas ... no es ideal, pero debería funcionar ... ¿Por qué puede' ¿Define que un tipo genérico debe ser una interfaz?

Por lo que vale, quiero esto porque Foo está haciendo reflexión donde necesita un tipo de interfaz ... Podría pasarlo como un parámetro normal y hacer la comprobación necesaria en la función en sí, pero esto parecía mucho más typesafe (y supongo que un poco más eficiente, ya que todos los controles se realizan en tiempo de compilación).

+3

En realidad, su IBase dea es la mejor que he visto hasta ahora. Desafortunadamente, no puede usarlo para interfaces que no le pertenecen. Todo lo que C# debería hacer es tener todas las interfaces heredadas de IOjbect como todas las clases heredan de Object. – Rhyous

Respuesta

98

Lo más cerca que puede hacer (excepto por su enfoque de interfaz base) es "where T : class", lo que significa tipo de referencia. No hay sintaxis para significar "cualquier interfaz".

Esto ("where T : class") se utiliza, por ejemplo, en WCF para limitar clientes a contratos de servicio (interfaces).

+3

buena respuesta, pero ¿tienes alguna idea * por qué * esta sintaxis no existe? Parece que sería una característica agradable de tener. –

+0

@StephenHolt: Creo que los creadores de .NET, al decidir qué restricciones permitir, se centraron en aquellas que permitirían que las clases y métodos genéricos hicieran cosas con tipos genéricos que de otro modo no podrían, en lugar de evitar que se utilicen. de maneras absurdas. Dicho esto, una restricción de 'interfaz' en' T' debería permitir las comparaciones de referencia entre 'T' y cualquier otro tipo de referencia, ya que las comparaciones de referencias están permitidas entre cualquier interfaz y casi cualquier otro tipo de referencia, y permitir comparaciones incluso en ese caso no planteas ningún problema – supercat

+0

@supercat otra aplicación útil de dicha restricción hipotética sería crear de manera segura un proxy para las instancias del tipo. Para la interfaz, se garantiza que es seguro, mientras que para las clases selladas fallará, al igual que para las clases con métodos no virtuales. –

19

No, en realidad, si piensa class y struct significa class es y struct s, está equivocado. class medios cualquier tipo de referencia (por ejemplo incluye interfaces también) y struct significa cualquier tipo de valor (por ejemplo struct, enum).

+1

¿No es esa la definición de la diferencia entre una clase y una estructura aunque: que cada clase es un tipo de referencia (y viceversa) y lo mismo para tipos de costo/valor –

+0

Mateo: Hay más para tipos de valor que las estructuras C#. Los enumeraciones, por ejemplo, son tipos de valores y coinciden con la restricción 'donde T: struct'. –

+0

Vale la pena señalar que los tipos de interfaz utilizados en las restricciones no implican 'clase', pero declarar una ubicación de almacenamiento de un tipo de interfaz realmente declara que la ubicación de almacenamiento es una referencia de clase que implementa ese tipo. – supercat

7

No puede hacer esto en ninguna versión de C#, ni en la próxima C# 4.0. Tampoco es una limitación de C#; no existe una restricción de "interfaz" en el CLR.

-4

Use una clase abstracta en su lugar. Por lo tanto, debería tener algo como:

public bool Foo<T>() where T : CBase; 
+7

No siempre puede reemplazar una interfaz con una clase abstracta ya que C# no admite herencia múltiple. – Sam

91

Sé que esto es un poco tarde, pero para aquellos que estén interesados ​​puede utilizar un control de tiempo de ejecución.

typeof(T).IsInterface 
+9

+1 por ser la única respuesta para señalar esto. Acabo de agregar una respuesta con un enfoque para mejorar el rendimiento al verificar cada tipo solo una vez en lugar de cada vez que se llama al método. – phoog

+8

La idea general de los genéricos en C# es tener seguridad en tiempo de compilación. Lo que sugieres también se puede realizar con un método no genérico '' Foo (Tipo de tipo) ''. –

+0

Me gusta el control del tiempo de ejecución. Gracias. –

2

Lo que han conformado con es el mejor que puede hacer:

public bool Foo<T>() where T : IBase; 
16

Para dar seguimiento a la respuesta de Robert, esto es incluso más tarde, pero se puede utilizar una clase auxiliar estática para hacer que el tiempo de ejecución comprobar una sola vez por cada tipo:

public bool Foo<T>() where T : class 
{ 
    FooHelper<T>.Foo(); 
} 

private static class FooHelper<TInterface> where TInterface : class 
{ 
    static FooHelper() 
    { 
     if (!typeof(TInterface).IsInterface) 
      throw // ... some exception 
    } 
    public static void Foo() { /*...*/ } 
} 

también observo que su solución "debe trabajar" no lo hace, de hecho, el trabajo. Considere:

public bool Foo<T>() where T : IBase; 
public interface IBase { } 
public interface IActual : IBase { string S { get; } } 
public class Actual : IActual { public string S { get; set; } } 

Ahora no hay nada que le para de llamar Foo así:

Foo<Actual>(); 

La clase Actual, después de todo, satisface la restricción IBase.

+0

Un constructor 'static' no puede ser' public', así que esto debería dar un error en tiempo de compilación. Además, su clase 'static' contiene un método de instancia, también es un error en tiempo de compilación. –

+0

Retrasado gracias a nawfal para corregir los errores notados por @JeppeStigNielsen – phoog

1

Traté de hacer algo similar y usé una solución alternativa: pensé en el operador implícito y explícito en la estructura: La idea es envolver el Tipo en una estructura que se pueda convertir en Tipo implícitamente.

Aquí es una estructura de este tipo:

estructura pública InterfaceType { Tipo _type privada;

public InterfaceType(Type type) 
{ 
    CheckType(type); 
    _type = type; 
} 

public static explicit operator Type(InterfaceType value) 
{ 
    return value._type; 
} 

public static implicit operator InterfaceType(Type type) 
{ 
    return new InterfaceType(type); 
} 

private static void CheckType(Type type) 
{ 
    if (type == null) throw new NullReferenceException("The type cannot be null"); 
    if (!type.IsInterface) throw new NotSupportedException(string.Format("The given type {0} is not an interface, thus is not supported", type.Name)); 
} 

}

uso básico:

// OK 
InterfaceType type1 = typeof(System.ComponentModel.INotifyPropertyChanged); 

// Throws an exception 
InterfaceType type2 = typeof(WeakReference); 

Hay que imaginarse su propio MECANISMO alrededor de esto, pero un ejemplo podría ser un método toma una InterfaceType en el parámetro en lugar de un tipo

this.MyMethod(typeof(IMyType)) // works 
this.MyMethod(typeof(MyType)) // throws exception 

Un método para anular que debería devolver tipos de interfaz:

public virtual IEnumerable<InterfaceType> GetInterfaces() 

tal vez hay cosas que hacer con los genéricos también, pero no me trataron

Esperamos que esto pueda ayudar o da ideas :-)

6

Si es posible, fui con una solución como esta. Solo funciona si desea que varias interfaces específicas (por ejemplo, aquellas a las que tiene acceso de origen) se pasen como un parámetro genérico, no cualquiera.

  • Dejo que mis interfaces, que entraron en duda, hereden una interfaz vacía IInterface.
  • que limitó el parámetro T genérica a ser de IInterface

En la fuente, que se ve así:

  • Cualquier interfaz que desea ser transferido como parámetro genérico:

    public interface IWhatever : IInterface 
    { 
        // IWhatever specific declarations 
    } 
    
  • IInterface:

    public interface IInterface 
    { 
        // Nothing in here, keep moving 
    } 
    
  • La clase en la que desea colocar el tipo de restricción:

    public class WorldPeaceGenerator<T> where T : IInterface 
    { 
        // Actual world peace generating code 
    } 
    
9

Desde hace algún tiempo he estado pensando acerca de las limitaciones de tiempo de compilación próximo, por lo que esta es una oportunidad perfecta para lanzar el concepto.

La idea básica es que si no puede hacer una comprobación de tiempo de compilación, debe hacerlo en el momento más temprano posible, que es básicamente el momento en que se inicia la aplicación. Si todos los controles están correctos, la aplicación se ejecutará; si falla un control, la aplicación fallará instantáneamente.

Comportamiento

El mejor resultado posible es que nuestro programa no compila si no se cumplen las restricciones. Desafortunadamente eso no es posible en la implementación actual de C#.

Lo mejor es que el programa se bloquea en el momento en que se inicia.

La última opción es que el programa se bloqueará en el momento en que se golpee el código. Este es el comportamiento predeterminado de .NET. Para mí, esto es completamente inaceptable.

Requisitos previos

Necesitamos tener un mecanismo de restricción, por lo que la falta de algo mejor ... vamos a utilizar un atributo. El atributo estará presente además de una restricción genérica para verificar si coincide con nuestras condiciones. Si no es así, damos un feo error.

Esto nos permite hacer cosas como esta en nuestro código:

public class Clas<[IsInterface] T> where T : class 

(He mantenido la where T:class aquí, porque yo siempre prefiero tiempo de compilación cheques a tiempo de ejecución de los controles)

Entonces, eso solo nos deja con un problema, que es verificar si todos los tipos que usamos coinciden con la restricción. ¿Qué tan difícil puede ser?

Vamos a romper hasta

tipos genéricos son siempre ya sea en una clase (/ estructura/interfaz) o en un método.

Activación de una restricción requiere que se lleve a cabo una de las siguientes cosas:

  1. tiempo de compilación, cuando se utiliza un tipo de un tipo (herencia, limitación genérica, miembro de la clase)
  2. tiempo de compilación, cuando se utiliza un tipo en un cuerpo del método
  3. Tiempo de ejecución, cuando se usa la reflexión para construir algo basado en la clase base genérica.
  4. Tiempo de ejecución, al usar la reflexión para construir algo basado en RTTI.

En este punto, me gustaría decir que siempre debe evitar hacer (4) en cualquier programa IMO. De todos modos, estas comprobaciones no lo respaldarán, ya que efectivamente significaría resolver el problema de detención.

Caso 1: utilizando un tipo de

Ejemplo:

public class TestClass : SomeClass<IMyInterface> { ... } 

Ejemplo 2:

public class TestClass 
{ 
    SomeClass<IMyInterface> myMember; // or a property, method, etc. 
} 

Básicamente esto implica la exploración de todos los tipos, de herencia, miembros, parámetros, etc, etcétera etcétera.Si un tipo es genérico y tiene una restricción, verificamos la restricción; si es una matriz, verificamos el tipo de elemento.

En este punto debo agregar que esto romperá el hecho de que, por defecto, .NET carga tipos 'flojo'. Al escanear todos los tipos, forzamos el tiempo de ejecución .NET para cargarlos todos. Para la mayoría de los programas, esto no debería ser un problema; aún así, si usa inicializadores estáticos en su código, puede encontrar problemas con este enfoque ... Dicho esto, no aconsejaría a nadie que haga esto de todos modos (excepto por cosas como esta :-), por lo que no debería dar tienes muchos problemas

Caso 2: el uso de un tipo en un método

Ejemplo:

void Test() { 
    new SomeClass<ISomeInterface>(); 
} 

Para comprobar esto sólo tenemos 1 opción: descompilar la clase, visita todas las fichas de miembro que se utilizan y si uno de ellos es el tipo genérico - verifica los argumentos.

Caso 3: Reflexión, tiempo de ejecución de la construcción genérica

Ejemplo:

typeof(CtorTest<>).MakeGenericType(typeof(IMyInterface)) 

supongo que es teóricamente posible comprobar esto con trucos similares como el caso (2), pero la aplicación de la misma es mucho más difícil (debe verificar si se llama MakeGenericType en alguna ruta de código). No voy a entrar en detalles aquí ...

Caso 4: Reflexión, tiempo de ejecución de RTTI

Ejemplo:

Type t = Type.GetType("CtorTest`1[IMyInterface]"); 

Este es el peor de los casos, y como he explicado antes generalmente una mala idea en mi humilde opinión. De cualquier manera, no hay una manera práctica de resolver esto usando cheques.

Prueba de la gran cantidad

Creación de un programa que pone a prueba el caso (1) y (2) dará lugar a algo como esto:

[AttributeUsage(AttributeTargets.GenericParameter)] 
public class IsInterface : ConstraintAttribute 
{ 
    public override bool Check(Type genericType) 
    { 
     return genericType.IsInterface; 
    } 

    public override string ToString() 
    { 
     return "Generic type is not an interface"; 
    } 
} 

public abstract class ConstraintAttribute : Attribute 
{ 
    public ConstraintAttribute() {} 

    public abstract bool Check(Type generic); 
} 

internal class BigEndianByteReader 
{ 
    public BigEndianByteReader(byte[] data) 
    { 
     this.data = data; 
     this.position = 0; 
    } 

    private byte[] data; 
    private int position; 

    public int Position 
    { 
     get { return position; } 
    } 

    public bool Eof 
    { 
     get { return position >= data.Length; } 
    } 

    public sbyte ReadSByte() 
    { 
     return (sbyte)data[position++]; 
    } 

    public byte ReadByte() 
    { 
     return (byte)data[position++]; 
    } 

    public int ReadInt16() 
    { 
     return ((data[position++] | (data[position++] << 8))); 
    } 

    public ushort ReadUInt16() 
    { 
     return (ushort)((data[position++] | (data[position++] << 8))); 
    } 

    public int ReadInt32() 
    { 
     return (((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18)); 
    } 

    public ulong ReadInt64() 
    { 
     return (ulong)(((data[position++] | (data[position++] << 8)) | (data[position++] << 0x10)) | (data[position++] << 0x18) | 
         (data[position++] << 0x20) | (data[position++] << 0x28) | (data[position++] << 0x30) | (data[position++] << 0x38)); 
    } 

    public double ReadDouble() 
    { 
     var result = BitConverter.ToDouble(data, position); 
     position += 8; 
     return result; 
    } 

    public float ReadSingle() 
    { 
     var result = BitConverter.ToSingle(data, position); 
     position += 4; 
     return result; 
    } 
} 

internal class ILDecompiler 
{ 
    static ILDecompiler() 
    { 
     // Initialize our cheat tables 
     singleByteOpcodes = new OpCode[0x100]; 
     multiByteOpcodes = new OpCode[0x100]; 

     FieldInfo[] infoArray1 = typeof(OpCodes).GetFields(); 
     for (int num1 = 0; num1 < infoArray1.Length; num1++) 
     { 
      FieldInfo info1 = infoArray1[num1]; 
      if (info1.FieldType == typeof(OpCode)) 
      { 
       OpCode code1 = (OpCode)info1.GetValue(null); 
       ushort num2 = (ushort)code1.Value; 
       if (num2 < 0x100) 
       { 
        singleByteOpcodes[(int)num2] = code1; 
       } 
       else 
       { 
        if ((num2 & 0xff00) != 0xfe00) 
        { 
         throw new Exception("Invalid opcode: " + num2.ToString()); 
        } 
        multiByteOpcodes[num2 & 0xff] = code1; 
       } 
      } 
     } 
    } 

    private ILDecompiler() { } 

    private static OpCode[] singleByteOpcodes; 
    private static OpCode[] multiByteOpcodes; 

    public static IEnumerable<ILInstruction> Decompile(MethodBase mi, byte[] ildata) 
    { 
     Module module = mi.Module; 

     BigEndianByteReader reader = new BigEndianByteReader(ildata); 
     while (!reader.Eof) 
     { 
      OpCode code = OpCodes.Nop; 

      int offset = reader.Position; 
      ushort b = reader.ReadByte(); 
      if (b != 0xfe) 
      { 
       code = singleByteOpcodes[b]; 
      } 
      else 
      { 
       b = reader.ReadByte(); 
       code = multiByteOpcodes[b]; 
       b |= (ushort)(0xfe00); 
      } 

      object operand = null; 
      switch (code.OperandType) 
      { 
       case OperandType.InlineBrTarget: 
        operand = reader.ReadInt32() + reader.Position; 
        break; 
       case OperandType.InlineField: 
        if (mi is ConstructorInfo) 
        { 
         operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes); 
        } 
        else 
        { 
         operand = module.ResolveField(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()); 
        } 
        break; 
       case OperandType.InlineI: 
        operand = reader.ReadInt32(); 
        break; 
       case OperandType.InlineI8: 
        operand = reader.ReadInt64(); 
        break; 
       case OperandType.InlineMethod: 
        try 
        { 
         if (mi is ConstructorInfo) 
         { 
          operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes); 
         } 
         else 
         { 
          operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()); 
         } 
        } 
        catch 
        { 
         operand = null; 
        } 
        break; 
       case OperandType.InlineNone: 
        break; 
       case OperandType.InlineR: 
        operand = reader.ReadDouble(); 
        break; 
       case OperandType.InlineSig: 
        operand = module.ResolveSignature(reader.ReadInt32()); 
        break; 
       case OperandType.InlineString: 
        operand = module.ResolveString(reader.ReadInt32()); 
        break; 
       case OperandType.InlineSwitch: 
        int count = reader.ReadInt32(); 
        int[] targetOffsets = new int[count]; 
        for (int i = 0; i < count; ++i) 
        { 
         targetOffsets[i] = reader.ReadInt32(); 
        } 
        int pos = reader.Position; 
        for (int i = 0; i < count; ++i) 
        { 
         targetOffsets[i] += pos; 
        } 
        operand = targetOffsets; 
        break; 
       case OperandType.InlineTok: 
       case OperandType.InlineType: 
        try 
        { 
         if (mi is ConstructorInfo) 
         { 
          operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), Type.EmptyTypes); 
         } 
         else 
         { 
          operand = module.ResolveMember(reader.ReadInt32(), mi.DeclaringType.GetGenericArguments(), mi.GetGenericArguments()); 
         } 
        } 
        catch 
        { 
         operand = null; 
        } 
        break; 
       case OperandType.InlineVar: 
        operand = reader.ReadUInt16(); 
        break; 
       case OperandType.ShortInlineBrTarget: 
        operand = reader.ReadSByte() + reader.Position; 
        break; 
       case OperandType.ShortInlineI: 
        operand = reader.ReadSByte(); 
        break; 
       case OperandType.ShortInlineR: 
        operand = reader.ReadSingle(); 
        break; 
       case OperandType.ShortInlineVar: 
        operand = reader.ReadByte(); 
        break; 

       default: 
        throw new Exception("Unknown instruction operand; cannot continue. Operand type: " + code.OperandType); 
      } 

      yield return new ILInstruction(offset, code, operand); 
     } 
    } 
} 

public class ILInstruction 
{ 
    public ILInstruction(int offset, OpCode code, object operand) 
    { 
     this.Offset = offset; 
     this.Code = code; 
     this.Operand = operand; 
    } 

    public int Offset { get; private set; } 
    public OpCode Code { get; private set; } 
    public object Operand { get; private set; } 
} 

public class IncorrectConstraintException : Exception 
{ 
    public IncorrectConstraintException(string msg, params object[] arg) : base(string.Format(msg, arg)) { } 
} 

public class ConstraintFailedException : Exception 
{ 
    public ConstraintFailedException(string msg) : base(msg) { } 
    public ConstraintFailedException(string msg, params object[] arg) : base(string.Format(msg, arg)) { } 
} 

public class NCTChecks 
{ 
    public NCTChecks(Type startpoint) 
     : this(startpoint.Assembly) 
    { } 

    public NCTChecks(params Assembly[] ass) 
    { 
     foreach (var assembly in ass) 
     { 
      assemblies.Add(assembly); 

      foreach (var type in assembly.GetTypes()) 
      { 
       EnsureType(type); 
      } 
     } 

     while (typesToCheck.Count > 0) 
     { 
      var t = typesToCheck.Pop(); 
      GatherTypesFrom(t); 

      PerformRuntimeCheck(t); 
     } 
    } 

    private HashSet<Assembly> assemblies = new HashSet<Assembly>(); 

    private Stack<Type> typesToCheck = new Stack<Type>(); 
    private HashSet<Type> typesKnown = new HashSet<Type>(); 

    private void EnsureType(Type t) 
    { 
     // Don't check for assembly here; we can pass f.ex. System.Lazy<Our.T<MyClass>> 
     if (t != null && !t.IsGenericTypeDefinition && typesKnown.Add(t)) 
     { 
      typesToCheck.Push(t); 

      if (t.IsGenericType) 
      { 
       foreach (var par in t.GetGenericArguments()) 
       { 
        EnsureType(par); 
       } 
      } 

      if (t.IsArray) 
      { 
       EnsureType(t.GetElementType()); 
      } 
     } 

    } 

    private void PerformRuntimeCheck(Type t) 
    { 
     if (t.IsGenericType && !t.IsGenericTypeDefinition) 
     { 
      // Only check the assemblies we explicitly asked for: 
      if (this.assemblies.Contains(t.Assembly)) 
      { 
       // Gather the generics data: 
       var def = t.GetGenericTypeDefinition(); 
       var par = def.GetGenericArguments(); 
       var args = t.GetGenericArguments(); 

       // Perform checks: 
       for (int i = 0; i < args.Length; ++i) 
       { 
        foreach (var check in par[i].GetCustomAttributes(typeof(ConstraintAttribute), true).Cast<ConstraintAttribute>()) 
        { 
         if (!check.Check(args[i])) 
         { 
          string error = "Runtime type check failed for type " + t.ToString() + ": " + check.ToString(); 

          Debugger.Break(); 
          throw new ConstraintFailedException(error); 
         } 
        } 
       } 
      } 
     } 
    } 

    // Phase 1: all types that are referenced in some way 
    private void GatherTypesFrom(Type t) 
    { 
     EnsureType(t.BaseType); 

     foreach (var intf in t.GetInterfaces()) 
     { 
      EnsureType(intf); 
     } 

     foreach (var nested in t.GetNestedTypes()) 
     { 
      EnsureType(nested); 
     } 

     var all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; 
     foreach (var field in t.GetFields(all)) 
     { 
      EnsureType(field.FieldType); 
     } 
     foreach (var property in t.GetProperties(all)) 
     { 
      EnsureType(property.PropertyType); 
     } 
     foreach (var evt in t.GetEvents(all)) 
     { 
      EnsureType(evt.EventHandlerType); 
     } 
     foreach (var ctor in t.GetConstructors(all)) 
     { 
      foreach (var par in ctor.GetParameters()) 
      { 
       EnsureType(par.ParameterType); 
      } 

      // Phase 2: all types that are used in a body 
      GatherTypesFrom(ctor); 
     } 
     foreach (var method in t.GetMethods(all)) 
     { 
      if (method.ReturnType != typeof(void)) 
      { 
       EnsureType(method.ReturnType); 
      } 

      foreach (var par in method.GetParameters()) 
      { 
       EnsureType(par.ParameterType); 
      } 

      // Phase 2: all types that are used in a body 
      GatherTypesFrom(method); 
     } 
    } 

    private void GatherTypesFrom(MethodBase method) 
    { 
     if (this.assemblies.Contains(method.DeclaringType.Assembly)) // only consider methods we've build ourselves 
     { 
      MethodBody methodBody = method.GetMethodBody(); 
      if (methodBody != null) 
      { 
       // Handle local variables 
       foreach (var local in methodBody.LocalVariables) 
       { 
        EnsureType(local.LocalType); 
       } 

       // Handle method body 
       var il = methodBody.GetILAsByteArray(); 
       if (il != null) 
       { 
        foreach (var oper in ILDecompiler.Decompile(method, il)) 
        { 
         if (oper.Operand is MemberInfo) 
         { 
          foreach (var type in HandleMember((MemberInfo)oper.Operand)) 
          { 
           EnsureType(type); 
          } 

         } 
        } 
       } 
      } 
     } 
    } 

    private static IEnumerable<Type> HandleMember(MemberInfo info) 
    { 
     // Event, Field, Method, Constructor or Property. 
     yield return info.DeclaringType; 
     if (info is EventInfo) 
     { 
      yield return ((EventInfo)info).EventHandlerType; 
     } 
     else if (info is FieldInfo) 
     { 
      yield return ((FieldInfo)info).FieldType; 
     } 
     else if (info is PropertyInfo) 
     { 
      yield return ((PropertyInfo)info).PropertyType; 
     } 
     else if (info is ConstructorInfo) 
     { 
      foreach (var par in ((ConstructorInfo)info).GetParameters()) 
      { 
       yield return par.ParameterType; 
      } 
     } 
     else if (info is MethodInfo) 
     { 
      foreach (var par in ((MethodInfo)info).GetParameters()) 
      { 
       yield return par.ParameterType; 
      } 
     } 
     else if (info is Type) 
     { 
      yield return (Type)info; 
     } 
     else 
     { 
      throw new NotSupportedException("Incorrect unsupported member type: " + info.GetType().Name); 
     } 
    } 
} 

Utilizando el código

Bueno , esa es la parte fácil :-)

// Create something illegal 
public class Bar2 : IMyInterface 
{ 
    public void Execute() 
    { 
     throw new NotImplementedException(); 
    } 
} 

// Our fancy check 
public class Foo<[IsInterface] T> 
{ 
} 

class Program 
{ 
    static Program() 
    { 
     // Perform all runtime checks 
     new NCTChecks(typeof(Program)); 
    } 

    static void Main(string[] args) 
    { 
     // Normal operation 
     Console.WriteLine("Foo"); 
     Console.ReadLine(); 
    } 
} 
Cuestiones relacionadas