C#8基本接口的默认方法调用解决方法

时间:2019-12-18 18:20:48

标签: c# reflection .net-core c#-8.0

根据https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods 可以使用以下语法显式调用基于接口的实现。

base(IInterfaceType).Method();

但这似乎尚未实现。

是否有解决方法(例如反射)来实现这一目标?


  

示例代码来说明问题

interface IA
{
    void M()
    {
        Console.WriteLine("IA.M");
    }
}

interface IB : IA
{
    void IA.M()
    {
        Console.WriteLine("IB.M");
    }
}

interface IC : IA
{
    void IA.M()
    {
        Console.WriteLine("IC.M");
    }
}

class D : IA, IB, IC
{
    public void M()
    {
        // base(IB).M(); Is not yet supported apparently
        ((IB)this).M(); // Throws stack overflow
    }
}

class Program
{
    static void Main(string[] args)
    {
        D d = new D();
        d.M();
    }
}

3 个答案:

答案 0 :(得分:4)

问题中的链接指向复制的from the proposal document in Github提案的版本

该功能为cut in April 2019

  

结论

     

剪切C#8的base()语法。我们打算在下一个主要版本中重新使用它。

设计会议文档解释说,如果没有运行时支持(无法及时提供),则该实现最多只能对C#有效,而对VB.NET则不可行。

  

如果运行时不存在B.M,则将调用A.M()。对于base()和interfaces,运行时不支持此功能,因此调用将引发异常。我们想在运行时中添加对此的支持,但是发布此版本太昂贵了。

     

我们有一些解决方法,但是它们没有我们想要的行为,也不是首选的代码生成器。尽管不是我们想要的那样,我们对C#的实现还是可行的,但是VB的实现会困难得多。而且,VB的实现要求接口的实现方法必须是公开的API表面。

至于无限递归,

public void M()
{
    ((IB)this).M(); // Throws stack overflow
}

基本上是

public void M()
{
    M(); // Throws stack overflow
}

通过接口调用默认接口成员的方式与显式实现接口方法的方式相同。此外,您要求在this而不是base上调用方法。

答案 1 :(得分:0)

有一种解决方法。 我使用GetFunctionPointer

  

警告不要使用此代码

static class BaseInterfaceInvocationExtension
{
    private static readonly string invalidExpressionMessage = "Invalid expression.";

    public static void Base<TInterface>(this TInterface owner, Expression<Action<TInterface>> selector)
    {
        if (selector.Body is MethodCallExpression methodCallExpression)
        {
            MethodInfo methodInfo = methodCallExpression.Method;
            string name = methodInfo.DeclaringType.FullName + "." + methodInfo.Name;
            Type type = owner.GetType();
            InterfaceMapping interfaceMapping = type.GetInterfaceMap(typeof(TInterface));
            var map = interfaceMapping;
            var interfaceMethod = map.InterfaceMethods.First(info =>
                info.Name == name);
            var functionPointer = interfaceMethod.MethodHandle.GetFunctionPointer();

            var x = methodCallExpression.Arguments.Select(expression =>
            {
                if (expression is ConstantExpression constantExpression)
                {
                    return constantExpression.Value;
                }
                var lambda = Expression.Lambda(Expression.Convert(expression, expression.Type));
                return lambda.Compile().DynamicInvoke();
            }).ToArray();
            Type actionType = null;
            if (x.Length == 0)
            {
                actionType = typeof(Action);
            }else if (x.Length == 1)
            {
                actionType = typeof(Action<>);
            }
            else if (x.Length == 2)
            {
                actionType = typeof(Action<,>);
            }
            var genericType = actionType.MakeGenericType(methodInfo.GetParameters().Select(t => t.ParameterType).ToArray());
            var instance = Activator.CreateInstance(genericType, owner, functionPointer);
            instance.GetType().GetMethod("Invoke").Invoke(instance, x);
        }
        else
        {
            throw new Exception(invalidExpressionMessage);
        }
    }
}

class D : IA, IB, IC
{
    public void M(int test)
    {
       this.Base<IB>(d => d.M(test));
    }
}

class Program
{
    static void Main(string[] args)
    {
        D d = new D();
        d.M(12);
        Console.ReadKey();
    }
}

答案 2 :(得分:0)

这是一种解决方法。这不是理想的。也许会帮助某人。

class C : IB
{
    public void IBM() => (this as IB).M();
}

class D : IA, IB, IC
{
    private C _c = new C();

    public void M()
    {
        _c.IBM();
    }
}