当前位置:网站首页>[raise bar C #] how to call the base of the interface
[raise bar C #] how to call the base of the interface
2022-06-10 22:51:00 【u012804784】
High quality resource sharing
| Learning route guidance ( Click unlock ) | Knowledge orientation | Crowd positioning |
|---|---|---|
| 🧡 Python Actual wechat ordering applet 🧡 | Progressive class | This course is python flask+ Perfect combination of wechat applet , From the deployment of Tencent to the launch of the project , Create a full stack ordering system . |
| Python Quantitative trading practice | beginner | Take you hand in hand to create an easy to expand 、 More secure 、 More efficient quantitative trading system |
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 , Its implementation class or sub interface is in When rewriting this method It cannot be 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> selector);
public static TReturn Base<TInterface, TReturn>(this TInterface instance, Expression> 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(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 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()),
\_ => 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 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 .
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 ns | 776 B |
| Method 2(FunctionPointer) | 1,391.9345 ns | 1,168 B |
| Method 3(InterfaceBaseInvoke.Fody) | 0.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. .
边栏推荐
- "The specified database user/password combination is rejected..." Solutions for
- Keras深度学习实战(8)——使用数据增强提高神经网络性能
- Opencv_100问_第二章 (6-10)
- Redis from entry to entry
- Blue Bridge Cup_ A fool sends a letter_ recursion
- TcaplusDB君 · 行业新闻汇编(五)
- Opencv_100问_第四章 (16-20)
- dc_ Study and summary of labs--lab1
- Sealem Finance打造Web3去中心化金融平台基础设施
- QT custom delegation
猜你喜欢
![C language internal skill cultivation [integer stored in memory]](/img/ef/7fec8e89f432603c503dddb42bd57f.png)
C language internal skill cultivation [integer stored in memory]

Sealem Finance打造Web3去中心化金融平台基础设施

Opencv_100问_第二章 (6-10)

Niuke: expression evaluation

Opencv_ 100 questions_ Chapter II (6-10)

LuoYongHao: if I were the person in charge, I could make apple products go up more than three steps

Redis from entry to entry

C语言内功修炼【整型在内存中的存储】

小微企业如何低成本搭建微官网

Opencv_100问_第三章 (11-15)
随机推荐
图像拼接摄像头拼接笔记
kubernetes 二进制安装(v1.20.15)(六)部署WorkNode节点
【TcaplusDB知识库】TcaplusDB机器初始化和上架介绍
TcaplusDB君 · 行业新闻汇编(三)
Opencv_ 100 questions_ Chapter IV (16-20)
Typescript - declaration files and built-in objects
Use of cocoeval function
[tcapulusdb knowledge base] Introduction to the machine where the tcapulusdb viewing process is located
Sealem Finance打造Web3去中心化金融平台基础设施
Implementation of simply untyped lambda calculus
Ability to deliver packages within D days [abstract class dichotomy -- Abstract judgment method]
dc_ Study and summary of labs--lab1
罗永浩:我要是负责人 能让苹果产品上去三个台阶不止
很流行的状态管理库 MobX 是怎么回事?
[tcapulusdb knowledge base] Introduction to tcapulusdb engine parameter adjustment
Opencv_ 100 questions_ Chapter II (6-10)
Whale conference sharing: what should we do if the conference is difficult?
[MySQL] summary of common data types
TcaplusDB君 · 行业新闻汇编(四)
Redis从入门到入土