Как найти интерфейс, где объявлен метод - PullRequest
0 голосов
/ 30 сентября 2011

Я использую Unity для перехвата.Поскольку у меня много интерфейсов, я вынужден использовать VirtualMethodInterceptor.В моем поведении я хотел бы реагировать только тогда, когда вызываемый метод был объявлен в интерфейсе определенного типа (со специальным атрибутом).Я думал, что MethodBase.DeclaringType решит мою проблему, но ведет себя иначе, чем я надеялся.Он возвращает тип реализации.

Я могу согласиться с тем, что это имеет смысл, поскольку метод может быть объявлен в нескольких интерфейсах, но должен быть способ легко получить их список.К сожалению, я еще не нашел.

Небольшой образец, показывающий мою проблему

public interface ISample
{
    void Do();
}

public class Sample : ISample
{
    public void Do()
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        var m = typeof(Sample).GetMethod("Do") as MethodBase;
        Console.WriteLine(m.DeclaringType.Name); // Prints "Sample"
    }
}

одно неловкое решение :

var interfaces = from i in input.MethodBase.DeclaringType.GetInterfaces()
                where i.GetCustomAttributes(typeof(CustomAttribute), true).Length > 0
                where i.GetMethod(input.MethodBase.Name, input.MethodBase.GetParameters().Select(p=>p.ParameterType).ToArray()) != null
                select i;

Ответы [ 3 ]

0 голосов
/ 03 октября 2011

В конце концов я использовал этот код:

var interfaces = from i in input.MethodBase.DeclaringType.GetInterfaces()
                 let parameters = input.MethodBase.GetParameters().Select(p=>p.ParameterType).ToArray()
                 where i.GetCustomAttributes(typeof(CustomAttribute), true).Length > 0
                 where i.GetMethod(input.MethodBase.Name, parameters) != null
                 select i;
0 голосов
/ 03 октября 2011

Я думаю, что вы застряли при перечислении интерфейсов; Я не видел способа получить доступ к определенному интерфейсу.

Кроме того, есть небольшой крайний случай, который может произойти, если интерфейс реализован явно. В этом случае (void ISample.Do()) MethodBase.Name будет полностью определенным именем метода (например, MyApp.ISample.Do), а не Do.

Единственное решение, которое я нашел, - убрать передовую информацию. Э.Г.

string methodName = input.MethodBase.Name;
int methodIndex = methodName.LastIndexOf('.');

if (methodIndex != -1)
{
    methodName = methodName.Substring(methodIndex + 1, 
        methodName.Length - methodIndex - 1);
}

var interfaces = from i in input.MethodBase.DeclaringType.GetInterfaces()
                 let parameters = input.MethodBase.GetParameters().
                     Select(p => p.ParameterType).ToArray()
                 where i.GetCustomAttributes(typeof(CustomAttribute), true).Length > 0
                 where i.GetMethod(methodName, parameters) != null
                 select i;

Кроме того, если есть другой метод с таким же именем и сигнатурой, я не уверен, как определить, был ли метод вызван через интерфейс, в отличие от публичного метода.

public class Sample : ISample
{
    public void Do()
    {
        // this is a public method
    }

    void ISample.Do()
    {
        // this is the interface implementation
    }
}

Полагаю, можно было бы искать другие методы с тем же именем и сигнатурой и различать их, просматривая другие свойства MethodBase.

т.е. public void Do() имеет IsHideBySig и IsPublic для true, в то время как void ISample.Do() имеет IsFinal, IsVirtual, IsPrivate, IsHideBySig для всех true. Но я не уверен, что этого достаточно для всех сценариев.

0 голосов
/ 30 сентября 2011

Единственное решение, которое я мог придумать (похоже, что вы не так неловко).

public static bool IsMethodDeclaredInInterface(MethodBase method, Type myInterface)
{
    var methodType = method.DeclaringType;
    var typeFilter = new TypeFilter((t, crit) =>
                                        {
                                            var critTypes = crit as Type[];
                                            return critTypes != null && critTypes.Any(ty => ty.FullName == t.FullName);
                                        });
    var res = methodType.FindInterfaces(typeFilter, new[] {myInterface});
    return res.Length > 0;
}
...