如何将接口用作 C# 泛型类型约束?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1096568/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:07:41  来源:igfitidea点击:

How can I use interface as a C# generic type constraint?

c#genericsinterfaceconstraints

提问by Matthew Scharley

Is there a way to get the following function declaration?

有没有办法获得以下函数声明?

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

ie. where T is an interface type (similar to where T : class, and struct).

IE。其中 T 是接口类型(类似于where T : class, 和struct)。

Currently I've settled for:

目前我已经解决了:

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

Where IBase is defined as an empty interface that is inherited by all my custom interfaces... Not ideal, but it should work... Why can't you define that a generic type must be an interface?

其中 IBase 定义为一个空接口,由我所有的自定义接口继承......不理想,但它应该工作......你为什么不能定义泛型类型必须是一个接口?

For what it's worth, I want this because Foois doing reflection where it needs an interface type... I could pass it in as a normal parameter and do the necessary checking in the function itself, but this seemed a lot more typesafe (and I suppose a little more performant, since all the checks are done at compiletime).

对于它的价值,我想要这个,因为Foo在需要接口类型的地方进行反射......我可以将它作为普通参数传入并在函数本身中进行必要的检查,但这似乎更类型安全(而且我假设性能更高一些,因为所有检查都是在编译时完成的)。

采纳答案by Marc Gravell

The closest you can do (except for your base-interface approach) is "where T : class", meaning reference-type. There is no syntax to mean "any interface".

你能做的最接近的事情(除了你的基本接口方法)是“ where T : class”,意思是引用类型。没有表示“任何接口”的语法。

This ("where T : class") is used, for example, in WCF to limit clients to service contracts (interfaces).

where T : class例如,此 (" ") 用于在 WCF 中将客户端限制为服务合同(接口)。

回答by Mehrdad Afshari

No, actually, if you are thinking classand structmean classes and structs, you're wrong. classmeans any reference type(e.g. includes interfaces too) and structmeans any value type(e.g. struct, enum).

不,实际上,如果您正在思考classstruct表示classes 和structs,那您就错了。class表示任何引用类型(例如也包括接口)并struct表示任何值类型(例如structenum)。

回答by Pavel Minaev

You cannot do this in any released version of C#, nor in the upcoming C# 4.0. It's not a C# limitation, either - there's no "interface" constraint in the CLR itself.

您不能在任何已发布的 C# 版本中执行此操作,也不能在即将发布的 C# 4.0 中执行此操作。这也不是 C# 限制 - CLR 本身没有“接口”约束。

回答by Eddie

Use an abstract class instead. So, you would have something like:

请改用抽象类。所以,你会有类似的东西:

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

回答by Robert

I know this is a bit late but for those that are interested you can use a runtime check.

我知道这有点晚了,但是对于那些感兴趣的人,您可以使用运行时检查。

typeof(T).IsInterface

回答by KevinDeus

What you have settled for is the best you can do:

你已经解决的是你能做的最好的事情:

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

回答by phoog

To follow up on Robert's answer, this is even later, but you can use a static helper class to make the runtime check once only per type:

要跟进罗伯特的回答,这甚至更晚,但您可以使用静态帮助程序类使运行时仅对每种类型进行一次检查:

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() { /*...*/ }
}

I also note that your "should work" solution does not, in fact, work. Consider:

我还注意到,您的“应该有效”的解决方案实际上不起作用。考虑:

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

Now there's nothing stopping you from calling Foo thus:

现在没有什么能阻止你这样调用 Foo:

Foo<Actual>();

The Actualclass, after all, satisfies the IBaseconstraint.

Actual班,毕竟,满足IBase约束。

回答by Charles HETIER

I tried to do something similar and used a workaround solution: I thought about implicit and explicit operator on structure: The idea is to wrap the Type in a structure that can be converted into Type implicitly.

我尝试做类似的事情并使用了一种解决方法:我考虑了结构上的隐式和显式运算符:这个想法是将 Type 包装在一个可以隐式转换为 Type 的结构中。

Here is such a structure:

这是一个这样的结构:

public struct InterfaceType { private Type _type;

公共结构接口类型 { 私有类型 _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));
}

}

}

basic usage:

基本用法:

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

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

You have to imagine your own mecanism around this, but an example could be a method taken a InterfaceType in parameter instead of a type

你必须想象你自己的机制,但一个例子可能是一个方法在参数中采用 InterfaceType 而不是类型

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

A method to override that should returns interface types:

一个应该返回接口类型的重写方法:

public virtual IEnumerable<InterfaceType> GetInterfaces()

There are maybe things to do with generics also, but I didn't tried

也可能与泛型有关,但我没有尝试

Hope this can help or gives ideas :-)

希望这可以帮助或提供想法:-)

回答by Ray

If possible, I went with a solution like this. It only works if you want several specific interfaces (e.g. those you have source access to) to be passed as a generic parameter, not any.

如果可能,我采用了这样的解决方案。它仅适用于您希望将多个特定接口(例如您可以访问源代码的接口)作为通用参数传递的情况,而不是任何。

  • I let my interfaces, which came into question, inherit an empty interface IInterface.
  • I constrained the generic T parameter to be of IInterface
  • 我让出现问题的接口继承了一个空的 interface IInterface
  • 我将通用 T 参数限制为 IInterface

In source, it looks like this:

在源代码中,它看起来像这样:

  • Any interface you want to be passed as the generic parameter:

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

    public interface IInterface
    {
        // Nothing in here, keep moving
    }
    
  • The class on which you want to put the type constraint:

    public class WorldPeaceGenerator<T> where T : IInterface
    {
        // Actual world peace generating code
    }
    
  • 您希望作为泛型参数传递的任何接口:

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

    public interface IInterface
    {
        // Nothing in here, keep moving
    }
    
  • 要放置类型约束的类:

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

回答by atlaste

For some time now I've been thinking about near-compile-time constraints, so this is a perfect opportunity to launch the concept.

一段时间以来,我一直在考虑接近编译时的限制,因此这是启动该概念的绝佳机会。

The basic idea is that if you cannot do a check compile time, you should do it at the earliest possible point in time, which is basically the moment the application starts. If all checks are okay, the application will run; if a check fails, the application will fail instantly.

基本思想是,如果你不能做一个检查编译时间,你应该在最早的时间点做,这基本上是应用程序启动的那一刻。如果所有检查都没有问题,应用程序就会运行;如果检查失败,应用程序将立即失败。

Behavior

行为

The best possible outcome is that our program doesn't compile if the constraints are not met. Unfortunately that's not possible in the current C# implementation.

最好的结果是,如果不满足约束,我们的程序就不会编译。不幸的是,这在当前的 C# 实现中是不可能的。

Next best thing is that the program crashes the moment it's started.

下一个最好的事情是程序在启动的那一刻就崩溃了。

The last option is that the program will crash the moment the code is hit. This is the default behavior of .NET. For me, this is completely unacceptable.

最后一个选项是程序将在代码被命中的那一刻崩溃。这是 .NET 的默认行为。对我来说,这是完全不能接受的。

Prerequirements

先决条件

We need to have a constraint mechanism, so for the lack of anything better... let's use an attribute. The attribute will be present on top of a generic constraint to check if it matches our conditions. If it doesn't, we give an ugly error.

我们需要有一个约束机制,所以没有更好的东西......让我们使用一个属性。该属性将出现在通用约束之上,以检查它是否符合我们的条件。如果没有,我们会给出一个丑陋的错误。

This enables us to do things like this in our code:

这使我们能够在我们的代码中做这样的事情:

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

(I've kept the where T:classhere, because I always prefer compile-time checks to run-time checks)

(我一直保留在where T:class这里,因为我总是更喜欢编译时检查而不是运行时检查)

So, that only leaves us with 1 problem, which is checking if all the types that we use match the constraint. How hard can it be?

因此,这仅给我们留下了 1 个问题,即检查我们使用的所有类型是否与约束匹配。它能有多难?

Let's break it up

让我们打破它

Generic types are always either on a class (/struct/interface) or on a method.

泛型类型总是在类(/struct/interface)或方法上。

Triggering a constraint requires you to do one of the following things:

触发约束需要您执行以下操作之一:

  1. Compile-time, when using a type in a type (inheritance, generic constraint, class member)
  2. Compile-time, when using a type in a method body
  3. Run-time, when using reflection to construct something based on the generic base class.
  4. Run-time, when using reflection to construct something based on RTTI.
  1. 编译时,在类型中使用类型时(继承、泛型约束、类成员)
  2. 编译时,在方法体中使用类型时
  3. 运行时,当使用反射构建基于通用基类的东西时。
  4. 运行时,当使用反射来构建基于 RTTI 的东西时。

At this point, I would like to state that you should always avoid doing (4) in any program IMO. Regardless, these checks won't support it, since it would effectively mean solving the halting problem.

在这一点上,我想声明您应该始终避免在任何程序 IMO 中执行 (4)。无论如何,这些检查不会支持它,因为它实际上意味着解决停机问题。

Case 1: using a type

案例 1:使用类型

Example:

例子:

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

Example 2:

示例 2:

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

Basically this involves scanning all types, inheritance, members, parameters, etc, etc, etc. If a type is a generic type and has a constraint, we check the constraint; if it's an array, we check the element type.

基本上这涉及扫描所有类型、继承、成员、参数等,等等。如果类型是泛型类型并且有约束,我们检查约束;如果是数组,我们检查元素类型。

At this point I must add that this will break the fact that by default .NET loads types 'lazy'. By scanning all the types, we force the .NET runtime to load them all. For most programs this shouldn't be a problem; still, if you use static initializers in your code, you might encounter problems with this approach... That said, I wouldn't advice anyone to do this anyways (except for things like this :-), so it shouldn't give you a lot of problems.

在这一点上,我必须补充一点,这将打破默认情况下 .NET 加载类型“懒惰”的事实。通过扫描所有类型,我们强制 .NET 运行时加载所有类型。对于大多数程序来说,这应该不是问题;尽管如此,如果您在代码中使用静态初始值设定项,您可能会遇到这种方法的问题......也就是说,我不建议任何人这样做(除了这样的事情:-),所以它不应该给你问题多多。

Case 2: using a type in a method

案例 2:在方法中使用类型

Example:

例子:

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

To check this we have only 1 option: decompile the class, check all member tokens that are used and if one of them is the generic type - check the arguments.

要检查这一点,我们只有 1 个选项:反编译类,检查所有使用的成员标记,如果其中之一是泛型类型 - 检查参数。

Case 3: Reflection, runtime generic construction

案例 3:反射、运行时泛型构造

Example:

例子:

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

I suppose it's theoretically possible to check this with similar tricks as case (2), but the implementation of it is much harder (you need to check if MakeGenericTypeis called in some code path). I won't go into details here...

我想理论上可以使用与情况(2)类似的技巧来检查它,但它的实现要困难得多(您需要检查是否MakeGenericType在某些代码路径中调用)。我不会在这里详述...

Case 4: Reflection, runtime RTTI

案例 4:反射,运行时 RTTI

Example:

例子:

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

This is the worst case scenario and as I explained before generally a bad idea IMHO. Either way, there's no practical way to figure this out using checks.

这是最坏的情况,正如我之前解释的那样,恕我直言,这是一个坏主意。无论哪种方式,都没有实用的方法可以使用检查来解决这个问题。

Testing the lot

测试批次

Creating a program that tests case (1) and (2) will result in something like this:

创建一个测试用例 (1) 和 (2) 的程序将导致如下结果:

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

Using the code

使用代码

Well, that's the easy part :-)

嗯,这是最简单的部分:-)

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