2009-07-08 10 views
124

C'è un modo per ottenere la seguente dichiarazione di funzione?Come posso utilizzare l'interfaccia come un vincolo di tipo generico C#?

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

ie. dove T è un tipo di interfaccia (simile a where T : class e struct).

Attualmente ho optato per:

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

Dove IBase è definita come un'interfaccia vuota che viene ereditato da tutte le mie interfacce personalizzate ... Non è l'ideale, ma dovrebbe funzionare ... Perché puo' t definisci che un tipo generico deve essere un'interfaccia?

Per quello che vale, lo voglio perché Foo sta facendo reflection in cui ha bisogno di un tipo di interfaccia ... Potrei passarlo come parametro normale e fare il controllo necessario nella funzione stessa, ma questo sembrava molto più typesafe (e suppongo un po 'più performante, dal momento che tutti i controlli sono fatti al compiletime).

+3

In realtà, la tua IBase dea è la migliore che abbia mai visto finora. Sfortunatamente, non puoi usarlo per le interfacce che non possiedi. Tutto ciò che C# dovrebbe fare è avere tutte le interfacce ereditate da IOjbect proprio come tutte le classi ereditano da Object. – Rhyous

risposta

98

Il più vicino possibile (tranne che per l'approccio di interfaccia di base) è "where T : class", che significa tipo di riferimento. Non c'è sintassi per "qualsiasi interfaccia".

Questo ("where T : class") viene utilizzato, ad esempio, in WCF per limitare i client ai contratti di servizio (interfacce).

+3

nice answer, ma avete qualche idea * perché * questa sintassi non esiste? Sembra che sarebbe una caratteristica piacevole. –

+0

@StephenHolt: Penso che i creatori di .NET, nel decidere quali vincoli consentire, fossero focalizzati su quelli che avrebbero permesso a classi e metodi generici di fare cose con tipi generici che altrimenti non potevano, piuttosto che impedire loro di essere usati in modi insensati. Detto questo, un vincolo 'interface' su' T' dovrebbe consentire confronti di confronto tra 'T' e qualsiasi altro tipo di riferimento, poiché i confronti di riferimento sono consentiti tra qualsiasi interfaccia e quasi ogni altro tipo di riferimento e consentendo confronti anche in quel caso non porre problemi. – supercat

+0

@supercat un'altra utile applicazione di tale ipotetico vincolo sarebbe quella di creare in modo sicuro un proxy per istanze del tipo. Per l'interfaccia è garantito che sia sicuro, mentre per le classi sigillate fallirebbe, come per le classi con metodi non virtuali. –

19

No, in realtà, se stai pensando class e struct significa class es e struct s, ti sbagli. class mezzi qualsiasi tipo di riferimento (ad esempio include interfacce troppo) e struct significa qualsiasi tipo di valore (ad esempio struct, enum).

+1

Non è la definizione della differenza tra una classe e una struct: ogni classe è un tipo di riferimento (e viceversa) e idem per i tipi stuct/value –

+0

Matthew: Ci sono più tipi di valore delle strutture C#. Enum, ad esempio, sono tipi di valore e corrispondono a 'dove T: struct' constraint. –

+0

Vale la pena notare che i tipi di interfaccia utilizzati nei vincoli non implicano 'class', ma dichiarare un percorso di archiviazione di un tipo di interfaccia dichiara in realtà che il percorso di archiviazione sia un riferimento di classe che implementa quel tipo. – supercat

7

Non è possibile farlo in nessuna versione rilasciata di C#, né nel prossimo C# 4.0. Non è nemmeno un limite di C# - non c'è alcun vincolo di "interfaccia" nel CLR stesso.

-4

Utilizzare invece una classe astratta. Quindi, si dovrebbe avere qualcosa di simile:

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

Non è sempre possibile sostituire un'interfaccia con una classe astratta poiché C# non supporta l'ereditarietà multipla. – Sam

91

So che questo è un po 'tardi, ma per coloro che sono interessati è possibile utilizzare un controllo di runtime.

typeof(T).IsInterface 
+9

+1 per essere l'unica risposta per farlo notare. Ho appena aggiunto una risposta con un approccio per migliorare le prestazioni controllando ogni tipo solo una volta anziché ogni volta che viene chiamato il metodo. – phoog

+8

L'intera idea di generici in C# è quella di avere una sicurezza in fase di compilazione. Quello che stai suggerendo può anche essere eseguito con un metodo non generico '' Foo (Tipo di tipo) ''. –

+0

Mi piace il controllo di runtime. Grazie. –

2

Ciò che avete optato per è il migliore che puoi fare:

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

Per seguire fino alla risposta di Robert, questo è ancora più avanti, ma è possibile utilizzare una classe di supporto statica per rendere il runtime controllare una sola volta per ogni 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() { /*...*/ } 
} 

Noto anche che la soluzione "dovrebbe funzionare" non lo fa, infatti, il lavoro. Considerate:

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; } } 

Ora non c'è nulla ti impedisce di chiamare Foo così:

Foo<Actual>(); 

La classe Actual, dopo tutto, soddisfa il vincolo IBase.

+0

Un costruttore 'static' non può essere' public', quindi questo dovrebbe dare un errore in fase di compilazione. Anche la tua classe 'static' contiene un metodo di istanza, anche questo è un errore in fase di compilazione. –

+0

In ritardo grazie a nawfal per correggere gli errori rilevati da @JeppeStigNielsen – phoog

1

Ho provato a fare qualcosa di simile e ho utilizzato una soluzione alternativa: ho pensato all'operatore implicito ed esplicito sulla struttura: l'idea è di racchiudere il tipo in una struttura che può essere convertita in tipo in modo implicito.

Ecco una tale struttura:

struct pubblico InterfaceType { tipo privato _type;

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 di base:

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

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

Devi immaginare il proprio mecanism intorno a questo, ma un esempio potrebbe essere un metodo preso un InterfaceType nel parametro invece di un tipo

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

Un metodo per eseguire l'override che deve restituire i tipi di interfaccia:

public virtual IEnumerable<InterfaceType> GetInterfaces() 

ci sono forse cose da fare con i farmaci generici anche, ma non ho provato

Spero che questo possa aiutare o dà idee :-)

6

Se possibile, sono andato con una soluzione come Questo. Funziona solo se vuoi che diverse interfacce specifiche (ad esempio quelle a cui hai accesso sorgente) vengano passate come parametro generico, non come alcuno.

  • Ho lasciato che le mie interfacce, che sono venute in dubbio, ereditano un'interfaccia vuota IInterface.
  • ho costretto il parametro generico T essere di IInterface

In origine, sembra che questo:

  • Qualsiasi interfaccia che si desidera essere passato come parametro generico:

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

    public interface IInterface 
    { 
        // Nothing in here, keep moving 
    } 
    
  • La classe su cui si desidera inserire il tipo di vincolo:

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

Da qualche tempo ho pensato a vincoli nei pressi-compile-time, quindi questa è una perfetta opportunità per lanciare il concetto.

L'idea di base è che se non è possibile eseguire un tempo di compilazione di controllo, è necessario farlo nel più breve tempo possibile, che è fondamentalmente il momento in cui inizia l'applicazione. Se tutti i controlli sono a posto, l'applicazione verrà eseguita; se un controllo fallisce, l'applicazione fallirà immediatamente.

Comportamento

Il miglior risultato possibile è che il nostro programma non viene compilato se i vincoli non sono soddisfatte. Sfortunatamente ciò non è possibile nell'attuale implementazione del C#.

La cosa migliore è che il programma si blocca nel momento in cui viene avviato.

L'ultima opzione è che il programma si bloccherà nel momento in cui viene colpito il codice. Questo è il comportamento predefinito di .NET. Per me, questo è assolutamente inaccettabile.

Requisiti preliminari

Abbiamo bisogno di avere un meccanismo di vincolo, così per la mancanza di meglio ... usiamo un attributo. L'attributo sarà presente sopra un vincolo generico per verificare se corrisponde alle nostre condizioni. In caso contrario, diamo un brutto errore.

Questo ci permette di fare cose come questa nel nostro codice:

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

(ho mantenuto la where T:class qui, perché preferisco sempre a tempo di compilazione controlli a run-time controlli)

Quindi, questo ci lascia solo con 1 problema, che sta controllando se tutti i tipi che usiamo corrispondono al vincolo. Quanto può essere difficile?

Dividiamolo fino

tipi generici sono sempre o su una classe (/ struct/interfaccia) o su un metodo.

Attivazione di un vincolo richiede di fare una delle seguenti cose:

  1. tempo di compilazione, quando si utilizza un tipo in un tipo (eredità, vincolo generico, membro della classe)
  2. in fase di compilazione, quando si utilizza un tipo in un corpo del metodo
  3. Tempo di esecuzione, quando si utilizza la riflessione per costruire qualcosa in base alla classe di base generica.
  4. Tempo di esecuzione, quando si utilizza la riflessione per costruire qualcosa basato su RTTI.

A questo punto, vorrei dire che si dovrebbe sempre evitare di fare (4) in qualsiasi programma IMO. Indipendentemente da ciò, questi controlli non lo supporteranno, poiché significherebbe in effetti risolvere il problema dell'arresto.

Caso 1: utilizzando un tipo

Esempio:

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

Esempio 2:

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

Fondamentalmente questo comporta la scansione di tutti i tipi, ereditarietà, membri, parametri, ecc, ecc. ecc.Se un tipo è di tipo generico e ha un vincolo, controlliamo il vincolo; se è un array, controlliamo il tipo di elemento.

A questo punto devo aggiungere che questo interromperà il fatto che per impostazione predefinita .NET carica i tipi "pigri". Analizzando tutti i tipi, imponiamo al runtime .NET di caricarli tutti. Per la maggior parte dei programmi questo non dovrebbe essere un problema; comunque, se si usano gli inizializzatori statici nel proprio codice, si potrebbero incontrare problemi con questo approccio ... Detto questo, non consiglierei a nessuno di farlo comunque (eccetto per cose del genere :-), quindi non dovrebbe dare hai un sacco di problemi.

Caso 2: utilizzando un tipo in un metodo

Esempio:

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

Per controllare questo abbiamo solo 1 opzione: decompilare la classe, controllare tutti i gettoni membri che vengono utilizzati e se uno di questi è il tipo generico: controlla gli argomenti.

Caso 3: Riflessione, runtime costruzione generico

Esempio:

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

suppongo che sia teoricamente possibile controllare questo con trucchi simili come caso (2), ma l'implementazione di esso è molto più difficile (è necessario verificare se MakeGenericType è chiamato in qualche percorso di codice). Non voglio entrare nei dettagli qui ...

Caso 4: Riflessione, runtime RTTI

Esempio:

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

Questo è lo scenario peggiore e, come ho spiegato prima in generale un cattiva idea IMHO. Ad ogni modo, non c'è un modo pratico per capirlo usando i controlli.

Prova del sacco

Creazione di un programma che verifica caso (1) e (2) si tradurrà in qualcosa di simile:

[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); 
     } 
    } 
} 

Utilizzando il codice

Bene , questa è la parte facile :-)

// 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(); 
    } 
} 
Problemi correlati