Почему не самый конкретный метод вызывается на основе типа параметра

#c# #oop #polymorphism #overloading

#c# #ооп #полиморфизм #перегрузка

Вопрос:

Здесь полный вопрос ОО новичка. У меня есть эти два метода в классе

 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 наследует сеанс)

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


}
  

Комментарии:

1. Есть ли конкретная причина, по которой вы вводите как ‘var’, а не ‘LateSession’?

2. @acron, в чем причина не вводить var ?

3. var != dynamic если это ваша причина против его использования. Это ярлык программиста. Компилятор по-прежнему применяет строгую типизацию и будет жаловаться, если не сможет определить фактический тип.

4. @acron, использование var здесь полностью эквивалентно использованию полного имени типа, это просто синтактический сахар…

5. @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», и жестко кодирует вызов метода с помощью аргумента сеанса.
Во втором примере аналогично компилятор генерирует жестко запрограммированный вызов другого метода.

В других языках вызов метода является «динамическим», что означает, что во время выполнения рассматриваются фактические типы. Методы, полиморфные по своим аргументам, называются «мультиметодами» (они полиморфны не только по классу, в котором они определены, но и по аргументам, следовательно, «мульти») (Редактировать: исправлены опечатки)

Комментарии:

1. Хорошее объяснение, я никогда этого не знал. Дальнейший поиск в Google показал, что ни C #, ни Java не допускают множественную отправку, но Lisp и Python допускают.

2. Но на самом деле, в этом случае все должно работать так, как ожидалось, поскольку тип известен во время компиляции, и должна быть вызвана правильная перегрузка. Вероятно, есть какая-то другая проблема, которая не очевидна из показанного примера…

3. Тип не «действительно» известен во время компиляции. Рассмотрим переменную, вводимую через параметр. Я только поместил «новый» там, чтобы мы, как читатели, знали, что происходит; 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) перегрузку и учитывать LateSession при (Session, SessionViewModel) перегрузке, например:

 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 # нет множественной отправки, однако вы можете реализовать двойную отправку, используя шаблон Visitor.

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

Ответ №5:

Какой тип dbSession после этого присваивания? Я бы предположил, что это то, что вы ожидаете, но это может быть Session .

Вам действительно нужно перегружать этот метод как дочерним, так и родительским классом по отдельности? Это кажется странным случаем, когда вам понадобятся оба, и, вероятно, приведет к путанице.

Комментарии:

1. Я поместил это там просто в качестве иллюстрации. Итак, dbSession имеет тип LateSession. Во время выполнения в моем коде я вижу, что dbSession имеет тип LateSession, а базовый класс — Session. Я хочу сделать это таким образом, чтобы у меня было разное поведение для разных типов. В противном случае я прибегну к очень неприятному переключению.