为啥不根据参数类型调用最具体的方法

Posted

技术标签:

【中文标题】为啥不根据参数类型调用最具体的方法【英文标题】:Why isn't the most specific method called based on type of parameter为什么不根据参数类型调用最具体的方法 【发布时间】:2011-08-24 23:18:27 【问题描述】:

这里的总 OO 菜鸟问题。我在一个类中有这两种方法

private void StoreSessionSpecific(LateSession dbSession, SessionViewModel session)

    session.LateSessionViewModel.Guidelines = dbSession.Guidelines.ToList();


private void StoreSessionSpecific(Session dbSession, SessionViewModel session )

        // nothing to do yet...

当我调用 StoreSessionSpecific 时,dbSession 的类型为 LateSession(LateSession 继承 Session)

var dbSession = new LateSession();
StoreSessionSpecific(dbSession, session);

我预计排名靠前的会被调用。由于 dbSession 是 LateSession 类型。

@Paolo Tedesco 这就是类的定义方式。

public class Session

    public int ID  get; set; 
    public int SessionTypeId  get; set; 
    public virtual SessionType SessionType  get; set; 
    [Required]
    public DateTime StartTime  get; set; 
    [Required]
    public DateTime EndTime  get; set; 
    // Session duration in minutes
    // public int SessionDuration  get; set; 
    public virtual ICollection<Attendee> Attendees  get; set; 



public class LateSession : Session



    public int MaxCriticalIncidentsPerUser  get; set; 
    public int MaxResultCriticalIncidents  get; set; 

    public virtual ICollection<Guideline> Guidelines  get; set; 



【问题讨论】:

您输入“var”而不是“LateSession”是否有特定原因? @acron,不输入var的原因是什么? var != dynamic 如果这是您反对使用它的原因。这是程序员的捷径。编译器仍然强制执行强类型,如果它不能确定实际类型会报错。 @acron,这里使用 var 完全等同于使用全类型名,只是语法糖... @Michail,该语言确实使用它首先找到的与参数匹配的方法。 【参考方案1】:

嗯,您的假设是合理的,并且有些语言的工作方式与您的想法一样。

你的代码是这样的吗:

Session s = new LateSession(); // the compiler only "knows" that s is of type Session
StoreSessionSpecific(s);

或者看起来像这样:

LateSession ls = new LateSession(); // the compiler knows that ls is in fact a LateSession
StoreSessionSpecific(ls);

在第一个示例中,编译器假装不知道“s”的实际类型,并使用 Session 参数对方法的调用进行硬编码。 在第二个示例中,编译器同样会生成对另一个方法的硬编码调用。

在其他语言中,方法调用是“动态的”,这意味着在运行时会考虑实际类型。在其参数上具有多态性的方法称为“多方法”(它们不仅在定义它们的类上是多态的,而且在参数上也是多态的,因此是“多”的) (编辑:修正错别字)

【讨论】:

一个很好的解释,我从来不知道这个。进一步的谷歌搜索显示,C# 和 Java 都不允许多重分派,但 Lisp 和 Python 允许。 但实际上,在这种情况下,事情应该按预期工作,因为在编译时类型是已知的,并且应该调用正确的重载。可能还有一些其他问题在显示的示例中并不明显...... 该类型在编译时不是“真正”知道的。考虑通过参数传递的变量。我只把“新”放在那里,我们作为读者都知道发生了什么;D【参考方案2】:

我认为问题出在您代码的其他地方。如果您尝试这个示例,一切都会按预期进行:

class Base  


class Derived : Base  


class Something 
    private void DoSomething(Base b) 
        Console.WriteLine("DoSomething - Base");
    
    private void DoSomething(Derived d) 
        Console.WriteLine("DoSomething - Derived");
    
    public void Test() 
        var d = new Derived();
        DoSomething(d);
    


static class Program 
    static void Main(params string[] args) 
        Something something = new Something();
        something.Test();
    

你能发布一个完整的例子吗?可能是类定义有问题...

【讨论】:

【参考方案3】:

我很抱歉不知道发生这种情况的具体原因为什么,但我知道如何解决它。

尝试失去(LateSession, SessionViewModel) 重载,并在(Session, SessionViewModel) 重载中考虑LateSession,如:

private void StoreSessionSpecific(Session dbSession, SessionViewModel session )

   if (dbSession is LateSession)  
      // handle as LateSession
    else  
      // handle as base-class Session
   

【讨论】:

【参考方案4】:

正如 Angel O'Sphere 所说,C# 没有多重分派,但是您可以使用访问者模式实现双重分派。

http://en.wikipedia.org/wiki/Visitor_pattern

【讨论】:

【参考方案5】:

分配后dbSession 的类型是什么?我会假设这是您所期望的,但它可能是 Session

另外,你真的需要用子类和父类重载这个方法吗?这似乎是一个奇怪的情况,您需要两者,并且可能会导致混乱。

【讨论】:

我把它放在那里只是为了说明。所以 dbSession 属于 LateSession 类型。在我的代码中运行时,我看到 dbSession 的类型是 LateSession,而基类是 Session。我想这样做,所以我对不同类型有不同的行为。否则我会求助于超级讨厌的开关。

以上是关于为啥不根据参数类型调用最具体的方法的主要内容,如果未能解决你的问题,请参考以下文章

类型参数的类型推断

java中的泛型

JVM方法调用过程

java中参数变量具体是啥,可以干啥,有啥作用,

java 泛型详解-绝对是对泛型方法讲解最详细的

Java-泛型摘抄