(C #), почему Visual Studio говорит, что это объект, а GetType говорит, что это Func <объект>?

2

С# новичок вопрос здесь. Следующий код (взятый из книги "С# от новичка к профессионалу" Кристиана Гросса, Apress) дает ошибку:

worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));

Причина в том, что метод DoAdd() не принимает данные аргументы.

public static Func<object> DoAdd(Func<object> cell1, Func<object> cell2) {...}

VS утверждает, что оба аргумента в вызове метода выше имеют тип object, тогда как метод принимает только Func<object>. Но значение обоих элементов рабочего листа имеет тип Func<object>:

worksheet.Add("A2", CellFactories.Static(10.0));

где этот метод Static просто возвращает заданное значение:

public static Func<object> Static(object value) { return () => value; }
// return type= Func<object>

Когда я использовал worksheet["A2"] как Func<object>, код действительно работает.

Но я кое-что не понимаю. Тип экземпляра объекта Func<object>. Я использовал метод GetType(), чтобы увидеть это доказательство, и сравните типы объектов исходных элементов с объектами литого (который принят):

Console.Writeline(worksheet["A2"].GetType());

// now cast to the correct type (why can't it do that implicitly, btw?)
Funk1 = worksheet["A2"] as Func<object>;

Console.Writeline(Funk1.GetType());

.. и они ВСЕ одинаковы! (Тип = System.Func'1[System.Object])

И даже когда я использую метод .Equals() для сравнения обоих типов, он возвращает true.

Тем не менее, VS видит первый экземпляр объекта как тип object в вызове метода. Зачем? Почему вызываемый метод "видит" аргумент как другой тип, чем GetType(), возвращается? (и если да, то что хорошего в методе GetType()?)

Большое спасибо за ваши советы/комментарии! (Трудно выучить язык, если примеры книг дают ошибку, и вы не видите причины - следовательно, получилось смутное впечатление, что что-то не так или с помощью GetType() или VS.)

Теги:
function
gettype

3 ответа

3

Вам нужно понять разницу между динамической типизацией и статической типизацией. У индексатора вашего объекта worksheet, скорее всего, есть статический тип object.

public object this[string cell]{get{...}set{...}}

Поскольку все объекты в С# inherit из типа object, ссылка на объект, хранящаяся в ячейке, может быть ссылкой на любой объект.

То есть, поскольку делегат (например, Func<T>) есть a object, он может быть сохранен в object ссылке:

Func<object> func = ()=>return "foo";
object o = func; // this compiles fine

И компилятор может понять все это, потому что он неявно понимает, что производный класс может быть сохранен в ссылке на базовый класс.

То, что компилятор не может сделать автоматически, определяет тип динамического типа или тип времени выполнения объекта.

Func<object> func = ()=>return "foo";
object o = func; // this compiles fine
func = o; // <-- ERROR

Компилятор не знает, что object, хранящийся в o, фактически имеет тип Func<object>. Это не должно отслеживать это. Это информация, которая должна быть проверена во время выполнения.

func = (Func<object>)o; // ok!

Вышеприведенная строка кода компилируется во что-то, что ведет себя аналогично этому:

if(o == null)
    func = null;
else if(typeof(Func<object>).IsAssignableFrom(func.GetType()))
    __copy_reference_address__(func, o); // made up function!  demonstration only
else throw new InvalidCastException();

Таким образом, любое действие (преобразование из одного типа в другое) можно проверить во время выполнения, чтобы убедиться, что оно действительное и безопасное.

  • 0
    +1 Абсолютно верно, однако, возможно, слишком сложно объяснить кого-то новичка в C # и / или программировании.
2

Другие дали точные и подробные ответы, но здесь я попытаюсь объяснить простым языком.

Когда вы пишете worksheet["A2"], вы действительно вызываете функцию-член worksheet

worksheet имеет функцию-член с именем [], которая принимает string и возвращает object

Подпись функции-члена [] выглядит как object this[string id]

Таким образом, функция worksheet["A2"] возвращает то, что является object. Это может быть int или string или многое другое. Все компиляторы знают, что это будет object.

В этом примере вы возвращаете Func<object>. Это нормально, потому что Func<object> является object. Однако затем вы передаете результат этой функции в качестве параметра в другую функцию.

Проблема здесь в том, что компилятор знает, что worksheet["A2"] возвращает object. Это так же специфично, как и компилятор. Поэтому компилятор видит, что worksheet["A2"] является объектом, и вы пытаетесь передать объект функции, которая не принимает object в качестве параметра.

Итак, здесь вы должны сообщить компилятору "hey dummy, a Func<object>", вернув возвращаемый объект в правильный тип.

worksheet.Add("C3", CellFactories.DoAdd(worksheet["A2"], worksheet["B1"]));

можно переписать как

worksheet.Add("C3", CellFactories.DoAdd((Func<object>)worksheet["A2"], (Func<object>)worksheet["B1"]));

Теперь компилятор знает, что, хотя функция [] возвращает object, она может обрабатывать ее как Func<object>.

Примечание: вы, вероятно, слишком много работаете на одной линии. Это может быть трудно для людей читать в будущем.

Почему вызываемый метод "видит" аргумент как другой тип, чем GetType() возвращает?

Компилятор знает только, что worksheet[] возвращает объект. Компилятор не может вызвать GetType() во время компиляции.

Что хорошего в методе GetType()?

Существует довольно много применений и злоупотреблений методом GetType(), но это совершенно другое обсуждение.;)

В общем, компилятор не предполагает ничего о типах. Это хорошо, потому что вы получаете ошибку времени компиляции, когда пытаетесь вставить этот квадратный привязку в круглое отверстие. Если компилятор не жаловался, эта ошибка будет отображаться во время выполнения, что означает, что вам, вероятно, понадобится unit test для обнаружения проблемы.

Вы можете обойти эту проблему, сообщив компилятору: "Я знаю, что эта вещь круглая привязка, поверьте мне". и затем он скомпилируется. Если вы лжете компилятору, вы получите ошибку во время выполнения этого кода.

Это называется "статическая типизация". Противоположная философия называется "динамическая типизация", где проверки типов выполняются во время выполнения. Static vs dynamic - это длительная дискуссия, и вам, вероятно, следует исследовать ее самостоятельно, если вам интересно.

  • 0
    +1: это хорошее прямое объяснение.
1

VS утверждает, что оба args в вызове метода выше имеют объект типа, тогда как метод принимает только Func. Но значение обоих элементов рабочего листа имеет тип Func

Да, но объявленный тип object. Компилятор не может знать, что фактический тип времени выполнения будет Func<object>, поэтому необходим явный приведение.

  • 0
    +1 Правильно, но можно использовать еще несколько объяснений.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню