简体   繁体   中英

c# testing if an object implements ISurface<T> of any Type using .GetType().GetInterface(typeof(ISurface<>).FullName)

I have a plugin architecture where I need to detect all plugins that implement interface ISurface<T> of any Type (ie test for ISurface<> ). I see there are several suggestions here using LINQ (eg this one ), and I am wondering if there are reasons to favor that over this:

.GetType().GetInterface("ISurface`1")

Edit : Regarding hardcoding the interface name, I assume those concerns would be mitigated if the name was extracted from the actual interface directly, as Tim also mentioned below:

.GetType().GetInterface(typeof(ISurface<>).FullName)

With .FullName there should be no problem with namespace ambiguity either. Hardcoding aside, I am primarily interested in the approach itself, as it seems shorter and cleaner than going through a series of type property checking / LINQ syntax. Then again, I don't know what's going on under the hood.

Here's how you would extract all the supported interfaces of type ISurface<anything> :

void Main()
{
    var supportedInterfaces =
        from intf in typeof(Test).GetInterfaces()
        where intf.IsGenericType
        let genericIntf = intf.GetGenericTypeDefinition()
        where genericIntf == typeof(ISurface<>)
        select intf;

    supportedInterfaces.Dump();
}

public class Test : ISurface<int>
{
}

public interface ISurface<T>
{
}

You can test this in LINQPad (the .Dump() extension method is a LINQPad extension).

If you check just by the 'simple' name of the interface (instead of the fully specified class name, incl namespaces), then you will have problems if you have another interface with the same name in another namespace.

If you call the GetInterface method with the fully specified class name, it should work ok.

MSDN specifically mentions that for generic types you should specify the mangled name, so I would expect it to work fine.

Of course, if it is the best method, can be debated. If you change the interface name, you will have problems.

Well, that's an implementation detail that could be subject to change. Plus you lose run-time type safely. I would rather stick with public methods - either by using Linq as you found or using reflection.

There are a few reasons that come to mind why you might not want to do this:

  1. If you refactor your code so that "ISurface`1" is no longer valid (eg add or remove type parameters or rename the interface), the compiler won't catch it. This can be solved by replacing it with typeof(ISurface<>).Name .
  2. If there's an ISurface<> in another namespace, it is ambiguous (or, at least, at first glance it appears so).

I'd probably go with the solution you linked, maybe wrapped in an extension method so that I could call it more simply, eg

public static Type GetInterface(this Type type, Type targetType)
{
    return type.GetInterfaces().SingleOrDefault(t => t.IsGenericType
                          && t.GetGenericTypeDefinition() == targetType);
}
public class Surface : ISurface<int> { /* ... */ }

typeof(Surface).GetInterface(typeof(ISurface<>)); //returns typeof(ISurface<int>)
typeof(NotASurface).GetInterface(typeof(ISurface<>)); //returns null

If you have any control over the interface in question, I would suggest that ISurface<T> should inherit from a non-generic ISurface . That type could in turn include a member which could indicate in various means the types for which ISurface<T> would be available (eg if one defined an interface ITypeRegistrar { Register<TType>(info about type);} , the non-generic ISurface could include a RegisterSupportedTypes(ITypeRegistrar registrar) method. Depending upon what exact information was associated with each type, a typical implementation might look like:

void RegisterSupportedTypes(ITypeRegistrar registrar)
{
   registrar.Register<Circle>(circleFactory);
   registrar.Register<Square>(squareFactory);
   registrar.Register<Rhombus>(rhombusFactory);
}

The client code would be receiving each item as its actual generic type, so using this approach would allow the program to operate without typecasts or Reflection. Because .NET has no concept of open generic delegates, the client code would have to manually implement the ITypeRegistrar interface (instead of using something like lambda syntax as a shortcut for creating a delegate) but the implemented method that could do something like store a reference to the supplied factory into a field of a static generic class without using Reflection, which is something a delegate wouldn't be able to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM