(C #), почему Visual Studio говорит, что это объект, а GetType говорит, что это Func <object>? - PullRequest
2 голосов
/ 17 июня 2009

C # вопрос новичка здесь. Следующий код (взят из книги «C # От новичка до профессионала» Кристиана Гросса, 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.)

Ответы [ 3 ]

3 голосов
/ 17 июня 2009

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

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

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

То есть, поскольку делегат (например, Func<T>) равен 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();

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

2 голосов
/ 17 июня 2009

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

Когда вы пишете 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 в качестве параметра.

Итак, здесь вы должны сказать компилятору "эй, дурак, это 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(), но это совершенно другое обсуждение. ;)

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

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

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

1 голос
/ 17 июня 2009

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

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

...