background

Released three years ago C#8.0 There is an important improvement in... Called Interface default implementation , From then on , The method defined in the interface can contain the method body , Default implementation . But for the default implementation of the interface , The implementation class or sub interface cannot override this method base call , Just like subclass overriding methods can be base.Method() like that . for example :

public interface IService
{
void Proccess()
{
Console.WriteLine("Proccessing");
}
} public class Service : IService
{
public void Proccess()
{
Console.WriteLine("Before Proccess");
base(IService).Proccess(); // Currently not supported , It is also the part that this article needs to discuss
Console.WriteLine("End Proccess");
}
}

The original C# The team listed this feature as a plan for the next step ( Click here to view details ), However, three years later, it has not been put on the agenda . The lack of this feature is undoubtedly a great limitation , Sometimes we do need interfaces base Call to implement certain requirements . This article will introduce two ways to implement it .

Method 1: Use reflection to find the interface implementation and call it

The core idea of this method is , Use reflection to find the interface implementation you need to call MethodInfo, And then build DynamicMethod Use OpCodes.Call Just call it .

First, we define the method signature used to represent the interface method base call .

public static void Base<TInterface>(this TInterface instance, Expression<Action<TInterface>> selector);
public static TReturn Base<TInterface, TReturn>(this TInterface instance, Expression<Func<TInterface, TReturn>> selector);

So the example in the previous section can be rewritten as :

public class Service : IService
{
public void Proccess()
{
Console.WriteLine("Before Proccess");
this.Base<IService>(m => m.Proccess());
Console.WriteLine("End Proccess");
}
}

So next , We need the basis lambda Expression to find its corresponding interface implementation , And then call it .

The first step is based on lambda Expression acquisition MethodInfo And parameters . It should be noted that , We also need to support the calling of attributes , In fact, attributes are also a method , So it can be handled together .

private static (MethodInfo method, IReadOnlyList<Expression> args) GetMethodAndArguments(Expression exp) => exp switch
{
LambdaExpression lambda => GetMethodAndArguments(lambda.Body),
UnaryExpression unary => GetMethodAndArguments(unary.Operand),
MethodCallExpression methodCall => (methodCall.Method!, methodCall.Arguments),
MemberExpression { Member: PropertyInfo prop } => (prop.GetGetMethod(true) ?? throw new MissingMethodException($"No getter in propery {prop.Name}"), Array.Empty<Expression>()),
_ => throw new InvalidOperationException("The expression refers to neither a method nor a readable property.")
};

The second step , utilize Type.GetInterfaceMap Get the interface implementation method to be called . The key points to note here are ,instanceType.GetInterfaceMap(interfaceType).InterfaceMethods All methods of the interface will be returned , So you can't just match by method name , Because there may be various overloads 、 The generic parameter 、 also new Keyword to declare a method with the same name , So you can follow the Method name + Declaration type + Method parameter + Method generic parameters The only way to determine ( In the following code block IfMatch The implementation of the )

internal readonly record struct InterfaceMethodInfo(Type InstanceType, Type InterfaceType, MethodInfo Method);

private static MethodInfo GetInterfaceMethod(InterfaceMethodInfo info)
{
var (instanceType, interfaceType, method) = info;
var parameters = method.GetParameters();
var genericArguments = method.GetGenericArguments();
var interfaceMethods = instanceType
.GetInterfaceMap(interfaceType)
.InterfaceMethods
.Where(m => IfMatch(method, genericArguments, parameters, m))
.ToArray(); var interfaceMethod = interfaceMethods.Length switch
{
0 => throw new MissingMethodException($"Can not find method {method.Name} in type {instanceType.Name}"),
> 1 => throw new AmbiguousMatchException($"Found more than one method {method.Name} in type {instanceType.Name}"),
1 when interfaceMethods[0].IsAbstract => throw new InvalidOperationException($"The method {interfaceMethods[0].Name} is abstract"),
_ => interfaceMethods[0]
}; if (method.IsGenericMethod)
interfaceMethod = interfaceMethod.MakeGenericMethod(method.GetGenericArguments()); return interfaceMethod;
}

The third step , Use the obtained interface method , structure DynamicMethod. The emphasis is on using OpCodes.Call, It means to call a method in a non - virtual way , Even if the method is virtual , And don't look for a rewrite of it , Instead, it calls itself directly .

private static DynamicMethod GetDynamicMethod(Type interfaceType, MethodInfo method, IEnumerable<Type> argumentTypes)
{
var dynamicMethod = new DynamicMethod(
name: "__IL_" + method.GetFullName(),
returnType: method.ReturnType,
parameterTypes: new[] { interfaceType, typeof(object[]) },
owner: typeof(object),
skipVisibility: true); var il = dynamicMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); var i = 0;
foreach (var argumentType in argumentTypes)
{
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldc_I4, i);
il.Emit(OpCodes.Ldelem, typeof(object));
if (argumentType.IsValueType)
{
il.Emit(OpCodes.Unbox_Any, argumentType);
}
++i;
}
il.Emit(OpCodes.Call, method);
il.Emit(OpCodes.Ret);
return dynamicMethod;
}

Last , take DynamicMethod The conversion to strongly typed delegates is complete . Considering the optimization of performance , The final delegate can be cached , You don't have to build it again next time .

The complete code points here

Method 2: Using function pointers

This method and method 1 Be the same in essentials while differing in minor points , The difference is that , In the method 1 The second step , That is, to find the interface method MethodInfo after , Get its function pointer , Then use this pointer to construct a delegate . This method is actually the method I first found , Method 1 Is its improvement . There is no more introduction here

Method 3: utilize Fody Interface methods are modified at compile time IL Of call call

Method 1 It's possible , But the visible performance loss is large , Even with caching . So I used Fody Write a plug-in InterfaceBaseInvoke.Fody.

The core idea is to find the target interface method at compile time , And then use call Just call it with the command . This minimizes performance loss . For the usage of this plug-in, please refer to Project introduction .

Performance testing

Method The average time Memory allocation
Of the parent class base call 0.0000 ns-
Method 1(DynamicMethod)691.3687 ns776 B
Method 2(FunctionPointer)1,391.9345 ns1,168 B
Method 3(InterfaceBaseInvoke.Fody0.0066 ns-

summary

This paper discusses several methods to realize the interface base Method called , Among them, the performance is InterfaceBaseInvoke.Fody The best , stay C# The official support has previously recommended . Welcome to use , Be careful , Thank you. .

【 Bowing C#】 How to implement the interface base Call more related articles

  1. Use base. Call the properties in the parent class

    Use base. Call the properties in the parent class public class parent { public string a; }public class child :parent { public string g ...

  2. loadrunner do webservice A simple call to an interface

    Today, I heard from Dashen webservice Interface , I simulated the actual combat according to what he said , There may be a lot more wrong , In general use webservice Interface , Will use soapui, But it has been used. loadrunner Later discovered lr Soon ...

  3. Calling remote services using interfaces ------ Using dynamic invocation of services , Realization .net It's like Dubbo How to play .

    Distributed microservices are now the first of many corporate architectures , As far as I know , quite a lot java The company structure is Maven+Dubbo+Zookeeper On the basis of . Dubbo yes Alibaba Open source distributed service framework , Its biggest feature is to press ...

  4. Java WebService Interface generation and invocation Graphic, &gt;【 turn 】【 To be adjusted 】

    webservice brief introduction : Web Service technology , It can make different applications run on different machines without the aid of additional . Specialized third-party software or hardware , You can exchange data or integrate with each other . basis Web Service Between the application of specification implementation ...

  5. java Interface docking —— Others call our interface to get data

    java Interface docking —— Others call our interface to get data , We need to develop several interfaces in our system , Provide the other party with interface specification documents , Including access to our interface address , And input parameter name and format , And the status of our return , Interface code : package ...

  6. .net WebServer Examples and calls ( Interface WSDL Dynamic invocation JAVA)

    newly build .asmx page using System; using System.Collections.Generic; using System.Linq; using System.Web; using ...

  7. php Of swoole Extension onclose and onconnect Interface is not called

    In use swoole There was a problem when writing an online chat example , I checked a lot of information , Now record here . By looking at swoole_server Interface documentation , Callback registration interface on There are clear notes in the : * swoole_server-& ...

  8. according to URL Get parameter value json Result set , Give an interface to others to call

    background : When testing the interface , It is often the back end get\post Request direct return json Result set , I really want to know how to implement it , Although my heart probably knows how to achieve , But it's better to do it yourself ! Let's take a look at the results first : The following is a web Of get Interface entry ...

  9. 《React Backstage management system real combat : 3、 ... and 》header Components : Page layout 、 Weather request interface and page call 、 Time format and use of timers 、 Exit function

    One . Layout and typesetting 1. Layout src/pages/admin/header/index.jsx import React,{Component} from 'react' import './header. ...

  10. webservice wsdl Interface configuration and call

    Preparation package WebRoot/WEB-INF/lib/axis2-adb-1.6.2.jarWebRoot/WEB-INF/lib/axis2-ant-plugin-1.6.2.jarWebRoot/WEB ...

Random recommendation

  1. Allegro It cannot be saved ( The prompt is related to the user or lock of )

    This happens unintentionally during use But when I reopened the file, I found brd Under the file is a .brd.lck file , Easily deleted , Return to normal ~ This is a bug Explain bug, The specific method will be studied carefully next time ~ Make it a good habit to save manually every few minutes , prevent b ...

  2. MVC Appears after upgrade &quot; Does not exist in the current context ViewBag&quot; To solve the problem

    Take your project from MVC4 Upgrade to MVC5, As a result, there are a lot of problems ,View The design environment has emerged " Does not exist in the current context ViewBag" The problem of : Although it does not affect the compilation , But I always feel bad about it , And there are no grammatical hints ...

  3. Give Way TextView There is a running lantern effect

    Only need TextView You can add some properties to : <?xml version="1.0" encoding="utf-8"?> <LinearLa ...

  4. weblogic11g Install cluster —— win2003 System 、 Single host

    weblogic11g Install cluster —— win2003 System . Single host Be careful : This is a weblogic11g  stay win2003 Under the system ( A mainframe ) Installation cluster of ,linux.hpux.aix And multiple hosts ...

  5. Java Programming idea - Chapter four exercises

    practice 1: Write a program , Print from 1 To 100 Value public class Print1To100{ public static void main(String args[]){ for(int i = 1 ...

  6. iOS Performance optimization of development

    1. Avoid being too large XIB When loading XIB I put all my things in the memory , Include any pictures : If there is one that won't be used immediately view, It will waste precious memory resources . When loading a reference to a picture or sound resource nib when ,nib ...

  7. VMWare Workstation: LAN PC Connect to the remote desktop or port in the virtual machine

    It's simple . Make an understanding : 1.NAT 2.VM Network card of , It's like a router Environmental Science : Physical router :192.168.0.1 PC1(win):192.168.0.2 PC2(win):192.168.0.3 PC2 in ...

  8. ubuntu Set up vpn

    Baidu has information http://jingyan.baidu.com/article/fa4125aca7f1b628ad709271.html 1. Set up VPN CONNECTION 2.configur ...

  9. Win8 cannot access xp Shared solutions ——win8 Internet neighbors visit other xp The computer needs a user name and password to cancel

    New clothes win8, The original win7 Normal connection xp Sharing of downloaders , But in win8 I can't access it when I go down to the Internet xp The share of , Show ” Username or password incorrect ”, And the password is clearly right, as shown in the figure : resolvent : Press Win+R Startup and operation , transport secp ...

  10. Microsoft Azure Storage architecture design

    SQL Azure brief introduction SQL Azure yes Azure The logical database of the storage platform , The physical database is still SQL Server. A physical one SQL Server Divided into logical pieces (partition), Each piece becomes ...