Перегрузка метода в C# ведет себя неожиданно при использовании ключевого слова params
Хорошо, вернемся к основам. Мне интересно, как правильно перегрузить метод с params
аргумент.
Вот мой сценарий. Я начинаю с моего обычного метода:
public void MyMethod(MyObject mo)
{
// method body
}
И я создал для него перегрузку, которая выглядит так:
public void MyMethod(MyObject mo, params string[] fields)
{
// new method body
MyMethod(mo);
}
Очевидное намерение для MyMethod(new MyObject());
выполнить оригинальный метод и MyMethod(new MyObject(), "field0"/*, etc...*/);
выполнить перегруженный метод. Но дело не в этом.
На самом деле происходит то, что MyMethod(new MyObject());
выполняет перегруженный метод!
Я не понимаю этого. В этом типе сценария, как мне выполнить оригинальный метод?
ОБНОВЛЕНИЕ с актуальным кодом
Итак, вот фактический код, который производит описанное поведение.
Class1Base.cs:
public class Class1Base
{
public virtual void MyMethod(MyObject ob)
{
Console.WriteLine("Called Class1Base");
}
}
Class1.cs:
public class Class1 : Class1Base
{
public override void MyMethod(MyObject ob)
{
Console.WriteLine("called overridden method");
}
public void MyMethod(MyObject ob, params string[] fields)
{
Console.WriteLine("called OVERLOADED method");
}
}
MyObject.cs:
public class MyObject
{
public int Id { get; set; }
public string Description { get; set; }
}
Затем, когда я выполняю этот код следующим образом:
var myClass = new Class1();
var myObject = new MyObject();
myClass.MyMethod(myObject);
myClass.MyMethod(null);
myClass.MyMethod(null, "string");
Консоль показывает:
called OVERLOADED method
called OVERLOADED method
called OVERLOADED method
Я бы ожидал, что это покажет:
called overridden method
called overridden method
called OVERLOADED method
Почему не так?
1 ответ
Я не думаю, что вы рассказываете нам всю историю. Раздел 7.3.5.2 спецификации C# 5 (под названием "Лучший функциональный член") говорит частично:
• В противном случае, если MP применим в своей обычной форме, а MQ имеет массив параметров и применим только в расширенной форме, тогда MP лучше, чем MQ.
Это, похоже, имеет место здесь, так как params
Версия должна быть "расширена" до массива нулевой длины. И на самом деле, пробуя ваш код локально, вы получите ожидаемый результат вызоваparams
версия.
Обновление: в ответ на ваши изменения ответ теперь ясен: вы вызываете методы из Class1
, что означает, что при выполнении разрешения перегрузки методы, помеченные как override
не рассматриваются изначально. И поскольку применим не-переопределенный метод (хотя и в расширенном виде), то этот метод выбран.
В частности, раздел 7.6.5.1 читать частично:
• Набор методов-кандидатов сокращен, чтобы содержать только методы из наиболее производных типов: для каждого метода CF в наборе, где C - это тип, в котором объявлен метод F, все методы, объявленные в базовом типе C, удаляются. из набора.
Базовый класс MyMethod()
исключен из набора кандидатов, поэтому алгоритм не будет выбран.
Точное обоснование такого поведения состоит в том, чтобы избежать проявления проблемы "хрупкого базового класса". Предположим, у нас была следующая иерархия классов:
class A
{
}
class B : A
{
public void MyMethod(object o) { }
}
И следующий call-сайт:
new B().MyMethod("a string");
Это, очевидно, решит MyMethod()
это занимает object
, Но теперь предположим, что создатель A
(возможно, кто работает в другой команде) решает A
должен иметь MyMethod()
, тоже. Поэтому они меняют свой класс:
class A
{
public void MyMethod(string s);
}
А теперь представьте, что произойдет, если мы не исключим методы из базовых типов. Ваш звонок, который первоначально был разрешен B.MyMethod()
вдруг решил бы A.MyMethod()
(так как string
это лучшее совпадение.) Дизайнеры C# не хотели позволять другому человеку в совершенно другой команде молча менять смысл вашего кода.
Для получения дополнительной информации о хрупких проблемах базового класса в блоге Эрика Липперта (старый) есть несколько постов на эту тему. Просто немного поищите.