从抽象类中重载调用方法

8
我需要调用一个抽象类实现的方法。但是编译器总是默认使用具体类中重载的方法,因为签名只需要继承类型。
看一下代码:
-- 这显然是我正在使用的实际代码的大大简化版本 ;)
public class ModelBase { }
public class ModelA : ModelBase { }
public interface IInterface<in TModel>
{
    void Do(TModel model);
}
public abstract class AbstractClass<TModel> : IInterface<TModel>
{
    public abstract void Do(TModel model);
}
public class ConcreteClass : AbstractClass<ModelA>, IInterface<ModelBase>
{
    public override void Do(ModelA model)
    {
        // I'd like to invoke this method
    }

    public void Do(ModelBase model)
    {
        // how do I invoke the method above??
        Do((ModelA)model);
    }
}

^^ 这会导致递归

我尝试了以下方法:

((IClass<ModelA>)this).Do((ModelA)model);

^^ 不会改变任何内容

base.Do((ModelA)model);

^^ 报错 "无法调用抽象基础成员: 'AbstractClass.Do(ModelA)'"

如何调用 "public override void Do(ModelA model)" 方法?


顺便说一下,如果我将类更改为以下内容

public class ConcreteClass : IInterface<ModelA>, IInterface<ModelBase>

它有效。


这看起来像是编译器重载解析器的一个 bug... 我错过了什么? - Luis Filipe
如果我可以问一下,为什么你有一个重载函数使用了基础类型,但是又假设这个类型是ModelA?按照这种重载的设置,我可能会给你一个ModelB。 - Mike Perrenoud
非常有价值的问题@neoistheone。基本上我有一个正在被请求的IRepository<Order>。提供了一个ConcreteRepository<ITOrder>。现在在Repository接口上调用Insert方法。即使它是逆变的,我也无法将IRepository<ITOrder>强制转换为IRepository<Order>。因此,我必须将IRepository<Order>继承添加到具体存储库中。 - Sam7
我认为发生这种情况的原因是由于重载解析规则,具体来自C#规范的7.5.5.1 "如果N相对于A适用(第7.4.2.1节),则T的基类型中声明的所有方法都将从集合中删除。" 在这种情况下,确实存在ConcreteClass上的有效方法,并且您想要调用的override方法实际上属于基础AbstractClass<TModel>,因此它被忽略了。编辑:此外,重载规则可能会将两种方法视为“同等适用”;不确定。 - Chris Sinclair
我同意@ChrisSinclair的看法。但我不会猜想抽象方法的实现被认为是基类的一部分! - Sam7
@Sam7:如果我没记错的话,该方法“属于”基类。不过我可能错了;我知道重载解析规则对于这些可爱的边缘情况非常棘手,例如DavidN链接,Jon Skeet引用了_其他_规则来处理类似的情况。但我不能确定这是否是_相同_的情况,或者是否适用相同的规则,或者我是否在这里误解或使用了_错误_的规则。只是猜测。 - Chris Sinclair
3个回答

9
在调用之前,将this转换为AbstractClass<ModelA>
public void Do(ModelBase model)
{
    ((AbstractClass<ModelA>)this).Do((ModelA)model);
}

1
是的,我也打算发同样的内容。这里有一篇很好的文章,可以帮助理解基本方法和实例方法的解析:https://dev59.com/72ct5IYBdhLWcg3wa8s_ - DavidN
谢谢!我用 '((IClass<ModelA>)this).Do((ModelA)model);' 差点就成功了 :) - Sam7

4

使用命名参数:

public class ConcreteClass : AbstractClass<ModelA>, IInterface<ModelBase>
{
    public override void Do(ModelA modelA) // change 'model' into 'modelA'
    {
        // I'd like to invoke this method
    }

    public void Do(ModelBase model)
    {
        // how do I invoke the method above??
        Do(modelA : (ModelA)model);
    }
}

现在,编译器知道你想调用 Do(ModelA) 方法而不是 Do(ModelBase) 方法。

3
尝试显式地实现接口。从现在开始,您只能从类型为 IInterface<ModelBase> 的引用中调用方法 IInterface<ModelBase>.Do(ModelBase model)
public class ModelBase { }
public class ModelA : ModelBase { }
public interface IInterface<in TModel>
{
    void Do(TModel model);
}
public abstract class AbstractClass<TModel> : IInterface<TModel>
{
    public abstract void Do(TModel model);
}
public class ConcreteClass : AbstractClass<ModelA>, IInterface<ModelBase>
{
    public override void Do(ModelA model)
    {
        Console.Write("Do - Override AbstractClass<ModelA> ");
    }

    void IInterface<ModelBase>.Do(ModelBase model)
    {
        Console.Write("Do - IInterface<ModelBase> ");
        Do(model as ModelA);
    }
}

void Main()
{
    var modelA = new ModelA();

    (new ConcreteClass() as IInterface<ModelBase>).Do(modelA); //output -> Do - IInterface<ModelBase> Do - Override AbstractClass<ModelA> 
    Console.WriteLine();
    new ConcreteClass().Do(modelA); //output -> Do - Override AbstractClass<ModelA> 
}

谢谢。我可能会选择这个解决方案。 :) - Sam7
顺便提一下,注意这个语句 Do(model as ModelA); 因为你无法确定变量 model 是 ModelA 的实例还是其子类的实例。 - Claudio Musumeci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接