Перегрузка двух функций с помощью параметра object и list <object>
Рассмотрим этот код:
static void Main(string[] args)
{
Log("Test");//Call Log(object obj)
Log(new List<string>{"Test","Test2"});;//Also Call Log(object obj)
}
public static void Log(object obj)
{
Console.WriteLine(obj);
}
public static void Log(List<object> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
В первой строке я вызываю журнал со строковым значением, и он вызывает Log(object obj)
, но во второй строке я вызываю Log
со списком строки new List<string>{"Test","Test2"}
, но компилятор вызывает Log(object obj)
вместо Log(List<object> objects)
.
Почему компилятор имеет такое поведение?
Как я могу вызвать второй журнал со списком строк?
Ответы
Ответ 1
A List<string>
не a List<object>
; однако List<string>
a object
- поэтому имеет смысл выбрать эту перегрузку. Попробуйте вместо этого:
public static void Log<T>(IList<T> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
или даже:
public static void Log<T>(IEnumerable<T> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
Вам также может понравиться:
public static void Log(params object[] objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
который можно использовать как:
Log("Test","Test2");
Ответ 2
Вариант ответа Марка Гравелла:
public static void Log(IReadOnlyList<object> objects)
{
foreach (var obj in objects)
{
Console.WriteLine(obj);
}
}
Это не добавляет ничего к этому конкретному примеру, но если вы хотите использовать индексированный доступ к коллекции, как это было возможно, с помощью List<T>
, это позволит вам сделать это так, чтобы IEnumerable<object>
этого не делал. (И да, есть оператор LINQ для индексированного доступа к перечислимым типам, но он неуклюж, а также может быть ужасно медленным. IReadOnlyList<object>
полезно, если вы хотите четко указать, что ваш метод требует эффективного индексированного доступа.)
Подобно версии Marc, которая использует IEnumerable<object
> как тип аргумента, это использует ковариацию - в отличие от List<T>
, T
ковариантно в IEnumerable<T>
и в IReadOnlyList<T>
. Это означает, что поскольку string
является object
, a IEnumerable<string>
является IEnumerable<object>
, а также IReadOnlyList<string>
является IReadOnlyList<object>
.
Важность важности только для чтения обоих интерфейсов. Вся причина, по которой ваш исходный пример терпит неудачу, заключается в том, что List<T>
поддерживает чтение и запись - если вы передадите мне List<object>
, я могу добавить что-нибудь к нему - a string
, a Giraffe
или все, что мне нравится. Вот почему List<string>
не является приемлемой заменой List<object>
- я не могу добавить Giraffe
в List<string>
, хотя я могу добавить его в List<object>
. Но поскольку IEnumerable<T>
и IReadOnlyList<T>
не позволяют добавлять объекты в коллекции, которые они представляют, все, что имеет значение, - это то, что вы можете вытащить, а не то, что вы можете вставить. Все, что выходит из коллекции, содержащей только string
будет object
, потому что все a object
.
И да, я знаю, что ваш исходный код не пытался добавить что-либо в список, но это не имеет значения - все С# в этом случае заботится о том, как выглядит подпись функции. И, указав IReadOnlyList<object>
, вы ясно заявляете, что никогда не будете пытаться изменить список, после чего С# знает, что он ОК, чтобы передать List<string>
.
Ответ 3
List<string>
нельзя отнести к List<Object>
.
Если у вас есть List<Object>
, вы можете добавлять к нему объекты любого типа.
Если у вас есть List<string>
, вы можете добавлять к нему только строки.
Следовательно, a List<string>
нельзя отнести к List<Object>
, потому что его нельзя использовать одинаково.
Ответ 4
Я думаю, это хороший пример Liskov Substitution Principal. LSP в своем простом объяснении утверждает, что если животное может укусить, то собака (которая является животным) также должна укусить.
Это похоже на силлогизм в логике, который гласит, что:
- Все животные едят
- Корова - животное.
- Таким образом, корова ест
Я думаю, здесь компилятор следует этому принципу, потому что:
- Все объекты могут регистрироваться (
public void Log (object obj) {}
)
-
List<string>
- это объект
- Таким образом,
List<string>
может использоваться как параметр этого метода и записываться в журнал.