您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

2026-03-31 09:271阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1561个文字,预计阅读时间需要7分钟。

您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

目录 + 背景 + 方法1:使用反射找到接口实现并进行调用 + 方法2:利用函数指针 + 方法3:利用Fody在编译时对接口方法进行IL调用 + 性能测试 + 总结 + 背景 + 三年前发布的C#接口应用

目录
  • 背景
    • 方法1:使用反射找到接口实现并进行调用
    • 方法2:利用函数指针
    • 方法3:利用Fody在编译时对接口方法进行IL的call调用
  • 性能测试
    • 总结

      背景

      在三年前发布的C#8.0中有一项重要的改进叫做接口默认实现,从此以后,接口中定义的方法可以包含方法体了,即默认实现。

      不过对于接口的默认实现,其实现类或者子接口在重写这个方法的时候不能对其进行base调用,就像子类重写方法是可以进行base.Method()那样。例如:

      public interface IService { void Proccess() { Console.WriteLine("Proccessing"); } } public class Service : IService { public void Proccess() { Console.WriteLine("Before Proccess"); base(IService).Proccess(); // 目前不支持,也是本文需要探讨的部分 Console.WriteLine("End Proccess"); } }

      当初C#团队将这个特性列为了下一步的计划(点此查看细节),然而三年过去了依然没有被提上日程。这个特性的缺失无疑是一种很大的限制,有时候我们确实需要接口的base调用来实现某些需求。本文将介绍两种方法来实现它。

      方法1:使用反射找到接口实现并进行调用

      这种方法的核心思想是,使用反射找到你需要调用的接口实现的MethodInfo,然后构建DynamicMethod使用OpCodes.Call去调用它即可。

      首先我们定义方法签名用来表示接口方法的base调用。

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

      所以上一节的例子就可以改写成:

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

      于是接下来,我们就需要根据lambda表达式找到其对应的接口实现,然后调用即可。

      第一步根据lambda表达式获取MethodInfo和参数。要注意的是,对于属性的调用我们也需要支持,其实属性也是一种方法,所以可以一并处理。

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

      第二步,利用Type.GetInterfaceMap获取到需要调用的接口实现方法。此处注意的要点是,instanceType.GetInterfaceMap(interfaceType).InterfaceMethods会返回该接口的所有方法,所以不能仅根据方法名去匹配,因为可能有各种重载、泛型参数、还有new关键字声明的同名方法,所以可以按照方法名+声明类型+方法参数+方法泛型参数唯一确定一个方法(即下面代码块中IfMatch的实现)

      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 =&gt; IfMatch(method, genericArguments, parameters, m)) .ToArray(); var interfaceMethod = interfaceMethods.Length switch { 0 =&gt; throw new MissingMethodException($"Can not find method {method.Name} in type {instanceType.Name}"), &gt; 1 =&gt; throw new AmbiguousMatchException($"Found more than one method {method.Name} in type {instanceType.Name}"), 1 when interfaceMethods[0].IsAbstract =&gt; throw new InvalidOperationException($"The method {interfaceMethods[0].Name} is abstract"), _ =&gt; interfaceMethods[0] }; if (method.IsGenericMethod) interfaceMethod = interfaceMethod.MakeGenericMethod(method.GetGenericArguments()); return interfaceMethod; }

      第三步,用获取到的接口方法,构建DynamicMethod。其中的重点是使用OpCodes.Call,它的含义是以非虚方式调用一个方法,哪怕该方法是虚方法,也不去查找它的重写,而是直接调用它自身。

      private static DynamicMethod GetDynamicMethod(Type interfaceType, MethodInfo method, IEnumerable&lt;Type&gt; 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; }

      最后,将DynamicMethod转为强类型的委托就完成了。考虑到性能的优化,可以将最终的委托缓存起来,下次调用就不用再构建一次了。

      han12345/c42de446a23aa9a17fb6abf905479f25" rel="external nofollow" target="_blank">完整的代码点这里

      方法2:利用函数指针

      这个方法和方法1大同小异,区别是,在方法1的第二步,即找到接口方法的MethodInfo之后,获取其函数指针,然后利用该指针构造委托。这个方法其实是我最初找到的方法,方法1是其改进。在此就不多做介绍了

      方法3:利用Fody在编译时对接口方法进行IL的call调用

      方法1虽然可行,但是肉眼可见的性能损失大,即使是用了缓存。于是乎我利用Fody编写了一个插件InterfaceBaseInvoke.Fody。

      其核心思想就是在编译时找到目标接口方法,然后使用call命令调用它就行了。这样可以把性能损失降到最低。该插件的使用方法可以参考项目介绍。

      您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

      性能测试

      方法平均用时内存分配父类的base调用0.0000 ns-方法1(DynamicMethod)691.3687 ns776 B方法2(FunctionPointer)1,391.9345 ns1,168 B方法3(InterfaceBaseInvoke.Fody)0.0066 ns-

      总结

      本文探讨了几种实现接口的base调用的方法,其中性能以InterfaceBaseInvoke.Fody最佳,在C#官方支持以前推荐使用,更多关于C#实现接口base调用的资料请关注自由互联其它相关文章!

      本文共计1561个文字,预计阅读时间需要7分钟。

      您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

      目录 + 背景 + 方法1:使用反射找到接口实现并进行调用 + 方法2:利用函数指针 + 方法3:利用Fody在编译时对接口方法进行IL调用 + 性能测试 + 总结 + 背景 + 三年前发布的C#接口应用

      目录
      • 背景
        • 方法1:使用反射找到接口实现并进行调用
        • 方法2:利用函数指针
        • 方法3:利用Fody在编译时对接口方法进行IL的call调用
      • 性能测试
        • 总结

          背景

          在三年前发布的C#8.0中有一项重要的改进叫做接口默认实现,从此以后,接口中定义的方法可以包含方法体了,即默认实现。

          不过对于接口的默认实现,其实现类或者子接口在重写这个方法的时候不能对其进行base调用,就像子类重写方法是可以进行base.Method()那样。例如:

          public interface IService { void Proccess() { Console.WriteLine("Proccessing"); } } public class Service : IService { public void Proccess() { Console.WriteLine("Before Proccess"); base(IService).Proccess(); // 目前不支持,也是本文需要探讨的部分 Console.WriteLine("End Proccess"); } }

          当初C#团队将这个特性列为了下一步的计划(点此查看细节),然而三年过去了依然没有被提上日程。这个特性的缺失无疑是一种很大的限制,有时候我们确实需要接口的base调用来实现某些需求。本文将介绍两种方法来实现它。

          方法1:使用反射找到接口实现并进行调用

          这种方法的核心思想是,使用反射找到你需要调用的接口实现的MethodInfo,然后构建DynamicMethod使用OpCodes.Call去调用它即可。

          首先我们定义方法签名用来表示接口方法的base调用。

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

          所以上一节的例子就可以改写成:

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

          于是接下来,我们就需要根据lambda表达式找到其对应的接口实现,然后调用即可。

          第一步根据lambda表达式获取MethodInfo和参数。要注意的是,对于属性的调用我们也需要支持,其实属性也是一种方法,所以可以一并处理。

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

          第二步,利用Type.GetInterfaceMap获取到需要调用的接口实现方法。此处注意的要点是,instanceType.GetInterfaceMap(interfaceType).InterfaceMethods会返回该接口的所有方法,所以不能仅根据方法名去匹配,因为可能有各种重载、泛型参数、还有new关键字声明的同名方法,所以可以按照方法名+声明类型+方法参数+方法泛型参数唯一确定一个方法(即下面代码块中IfMatch的实现)

          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 =&gt; IfMatch(method, genericArguments, parameters, m)) .ToArray(); var interfaceMethod = interfaceMethods.Length switch { 0 =&gt; throw new MissingMethodException($"Can not find method {method.Name} in type {instanceType.Name}"), &gt; 1 =&gt; throw new AmbiguousMatchException($"Found more than one method {method.Name} in type {instanceType.Name}"), 1 when interfaceMethods[0].IsAbstract =&gt; throw new InvalidOperationException($"The method {interfaceMethods[0].Name} is abstract"), _ =&gt; interfaceMethods[0] }; if (method.IsGenericMethod) interfaceMethod = interfaceMethod.MakeGenericMethod(method.GetGenericArguments()); return interfaceMethod; }

          第三步,用获取到的接口方法,构建DynamicMethod。其中的重点是使用OpCodes.Call,它的含义是以非虚方式调用一个方法,哪怕该方法是虚方法,也不去查找它的重写,而是直接调用它自身。

          private static DynamicMethod GetDynamicMethod(Type interfaceType, MethodInfo method, IEnumerable&lt;Type&gt; 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; }

          最后,将DynamicMethod转为强类型的委托就完成了。考虑到性能的优化,可以将最终的委托缓存起来,下次调用就不用再构建一次了。

          han12345/c42de446a23aa9a17fb6abf905479f25" rel="external nofollow" target="_blank">完整的代码点这里

          方法2:利用函数指针

          这个方法和方法1大同小异,区别是,在方法1的第二步,即找到接口方法的MethodInfo之后,获取其函数指针,然后利用该指针构造委托。这个方法其实是我最初找到的方法,方法1是其改进。在此就不多做介绍了

          方法3:利用Fody在编译时对接口方法进行IL的call调用

          方法1虽然可行,但是肉眼可见的性能损失大,即使是用了缓存。于是乎我利用Fody编写了一个插件InterfaceBaseInvoke.Fody。

          其核心思想就是在编译时找到目标接口方法,然后使用call命令调用它就行了。这样可以把性能损失降到最低。该插件的使用方法可以参考项目介绍。

          您的问题似乎不完整,您是想询问关于C语言编程的某个具体问题吗?比如C语言的语法、编程技巧、项目开发等。请提供更具体的信息,这样我才能给出更准确的回答。

          性能测试

          方法平均用时内存分配父类的base调用0.0000 ns-方法1(DynamicMethod)691.3687 ns776 B方法2(FunctionPointer)1,391.9345 ns1,168 B方法3(InterfaceBaseInvoke.Fody)0.0066 ns-

          总结

          本文探讨了几种实现接口的base调用的方法,其中性能以InterfaceBaseInvoke.Fody最佳,在C#官方支持以前推荐使用,更多关于C#实现接口base调用的资料请关注自由互联其它相关文章!