Проблема здесь в том, что shower
- это тип interface
. Типы интерфейсов в Go удерживают фактическое значение и его динамический тип. Подробнее об этом: Законы отражения # Представление интерфейса .
Выбранный фрагмент содержит 2 значения не nil
. Второе значение представляет собой значение интерфейса, пару (значение; тип), содержащее значение nil
и тип *display
. Цитирование из спецификации языка g1] Go: Операторы сравнения :
Значения интерфейса сопоставимы. Два значения интерфейса равны, если они имеют одинаковые динамические типы и равные динамические значения или оба имеют значение
blockquote>nil
.Поэтому, если вы сравните его с
nil
, это будетfalse
. Если вы сравните его со значением интерфейса, представляющим пару(nil;*display)
, это будетtrue
:if x == (*display)(nil) { panic("everything ok, nil found") }
Это кажется неосуществимым, так как вам нужно знать, какой тип интерфейса имеет.
Почему это реализовано таким образом?
Интерфейсы, в отличие от других конкретных типов (не-интерфейсы), могут содержать значения разных типов конкретных типов (разные статические типы). Время выполнения должно знать динамический или runtime-тип значения, хранящегося в переменной типа интерфейса.
interface
- это всего лишь набор методов, любой тип реализует его если одни и те же методы являются частью метода , заданного типа. Существуют типы, которые не могут бытьnil
, напримерstruct
или пользовательский тип сint
в качестве базового типа. В этих случаях вам не нужно будет сохранять значениеnil
этого конкретного типа.Но любой тип также включает конкретные типы, где
nil
является действительным (например, фрагменты, карты, каналы, все типы указателей), поэтому, чтобы сохранить значение во время выполнения, которое удовлетворяет интерфейсу, разумно поддерживать сохранениеnil
внутри интерфейса. Но помимоnil
внутри интерфейса мы должны хранить его динамический тип, поскольку значениеnil
не несет такую информацию. Альтернативным вариантом будет использованиеnil
в качестве самого значения интерфейса, когда значение, которое будет храниться в нем,nil
, но это решение недостаточно, так как оно потеряет информацию о динамическом типе.Некоторые говорят, что интерфейсы Go являются динамически типизированными, но это вводит в заблуждение. Они статически типизированы: переменная типа интерфейса всегда имеет один и тот же статический тип, и хотя во время выполнения значение, хранящееся в переменной интерфейса, может изменять тип, это значение всегда будет удовлетворять интерфейсу.
blockquote>В общем случае, если вы хотите указать
nil
для значения типаinterface
, используйте явное значениеnil
, а затем вы можете проверить равенствоnil
. Наиболее распространенным примером является встроенный типerror
, который является интерфейсом с одним методом. Всякий раз, когда нет ошибки, вы явно устанавливаете или возвращаете значениеnil
, а не значение какой-либо конкретной (неинтерфейсной) ошибки типа типа (что было бы очень плохой практикой, см. Демонстрацию ниже).В вашем примере путаница возникает из фактов, что:
- вы хотите иметь значение в качестве типа интерфейса (
shower
)- , но значение, которое вы хотите хранить в срезе не тип
shower
, а конкретный типПоэтому, когда вы помещаете
*display
тип в срезshower
, будет создано значение интерфейса, которое представляет собой пару (значение; тип), где значение равноnil
, а тип -*display
. Значение внутри пары будетnil
, а не значением самого интерфейса. Если вы поместите значениеnil
в срез, то значение интерфейса себя будетnil
, а условиемx == nil
будетtrue
.Демонстрация
См. этот пример: Игровая площадка
type MyErr string func (m MyErr) Error() string { return "big fail" } func doSomething(i int) error { switch i { default: return nil // This is the trivial true case case 1: var p *MyErr return p // This will be false case 2: return (*MyErr)(nil) // Same as case 1 case 3: var err error // Zero value is nil for the interface return err // This will be true because err is already interface type case 4: var p *MyErr return error(p) // This will be false because the interface points to a // nil item but is not nil itself. } } func main() { for i := 0; i <= 4; i++ { err := doSomething(i) fmt.Println(i, err, err == nil) } }
Выход:
0
true 1 false 2 false 3 true 4 false В случае 2 указатель
nil
но сначала он преобразуется в тип интерфейса (error
), поэтому создается значение интерфейса, которое содержит значениеnil
и тип*MyErr
, поэтому значение интерфейса не являетсяnil
.
Да, это было бы лучшим, кроме Вы опустите , как другие указали. Для заинтересованных объяснением позади отсутствия значений параметров по умолчанию, см. объяснение @Giovanni Galbo.$
s на названиях параметра
Относительно выборки от c# часто задаваемых вопросов:
Большинство проблем, перечисленных там, было решено для VB.Net (конкретно intellisense и проблемы комментариев xml), означая, что они - действительно отвлекающие маневры - существует код, доступный команде C#, которая решит проблему.
Другая причина имеет отношение к тому, чтобы вынуждать пользователя класса перекомпилировать, но это - определенный отвлекающий маневр, также. Если Вы изменяете значение по умолчанию в своем классе платформы, и пользователь не должен перекомпилировать, Вы рискуете пользователем, не знающим что измененное значение по умолчанию. Теперь у Вас есть потенциальная ошибка в коде, который не показывает вплоть до времени выполнения. Другими словами, альтернатива для перегрузки функции, по крайней мере, как плохо. Конечно, это также предполагает определенную реализацию функции, но это - реализация, предложенная в часто задаваемых вопросах.
Поэтому необходимо весить, остающаяся причина ("пытаются ограничить волшебство") по сравнению с фактом (который они подтверждают), что запись перегрузок "немного менее удобна". Лично, я говорю, вставляет функцию и позволяют программисту решить, использовать ли ее.
Только удовлетворить некоторое любопытство:
От того, Почему не делает параметров поддержки C# по умолчанию?:
На языках, таких как C++, значение по умолчанию может быть включено как часть объявления метода:
освободите Процесс (Сотрудник сотрудника, bool премия = ложь)
Этот метод можно назвать любым с:
a. Процесс (сотрудник, верный);
или
a. Процесс (сотрудник);
во втором случае премия параметра имеет значение false.
C# не имеет этой функции.
Одна причина у нас нет этой функции, связана с определенной реализацией функции. В мире C++, когда пользователь пишет:
a. Процесс (сотрудник);
компилятор генерирует
a.process (сотрудник, ложь);
Другими словами, компилятор берет значение по умолчанию, которое указано в прототипе метода и помещает его в вызов метода - это - так же, как если бы пользователь записал 'ложь' как второй параметр. Нет никакого способа изменить то значение по умолчанию, не вынуждая пользователя класса перекомпилировать, который неудачен.
Перегружающаяся модель работает лучше в этом отношении. Автор платформы просто определяет два отдельных метода и единственный параметр, который каждый называет методом с двумя параметрами. Это сохраняет значение по умолчанию в платформе, где это может быть изменено при необходимости.
Для компилятора было бы возможно взять что-то как определение C++ и произвести перегрузки, но существует несколько проблем с тем подходом.
Первый - то, что корреляция между кодом, который пользователь пишет и код компилятор, генерирует, менее очевидно. Мы обычно пытаемся ограничить волшебство, если это возможно, поскольку оно мешает программистам. Второй выпуск имеет отношение к вещам как комментарии документа XML и intellisense. Компилятор должен был бы иметь специальные правила для того, как он генерирует комментарии документа для перегруженных методов, и intellisense должен был бы иметь ум для сворачивания перегруженных методов в отдельный метод.
Запись перегрузок самостоятельно немного менее удобна, но мы думаем, что это - приемлемое решение.
Параметры по умолчанию являются частью C++, но с параметров по умолчанию C# 3.5 все еще не поддерживаются - необходимо будет перегрузиться. Они были доступны в VB.Net с тех пор 1.0.
Как указано, это не в настоящее время доступно в C# однако, они будут присутствовать в C# 4.0, как Sam Ng обсуждает на своем блоге:
Да.
Или приправление карри.
Или абстракция в класс и использование значений по умолчанию там.
Нет, AFAIK C# не поддерживает переопределение, и да, который является рекомендуемым способом выполнить тот же эффект.
самка это не делает задание?
void foo(int x):this(x, 0){}
void foo(int x, int y){
// code here
}