Ответ 1
Я предполагаю, что компилятор сначала пытается определить, к какому ресурсу обращаются, и только затем проверяет, является ли он get или set.
Ваша догадка правильная.
Вопрос в том, почему? Почему бы не исключить тех кандидатов, которые не предоставляют геттера? Почему бы не решить фактические методы get/set и игнорировать факт, что это свойство?
Компилятор может разрешить все кандидаты на свойства, а затем исключить те, которые не предоставляют get - почему он этого не делает? - sinelaw
Потому что языковые дизайнеры так не проектировали? - Роберт Харви
@sinelaw Потому что это не так, как определяется язык С#. Дело не в том, что этого не может быть сделано, просто это не сделано. - user2864740
Я уверен, что они спроектировали его таким образом (вряд ли они упустили эту ситуацию) - но какие рассуждения? - sinelaw
@sinelaw Предположительно из-за того, что они не чувствовали, что преимущество такой функции может привести к дополнительной сложности в ее разработке. - p.s.w.g
pswg находится на правильном пути здесь, но мы можем быть более конкретным.
Основной принцип проектирования здесь анализ исходит изнутри наружу без учета "контекстных реплик". Это сбивает с толку читателя и сложно для компилятора и разработчика IDE, когда значение выражения зависит от его непосредственного контекста. То, что мы хотим сделать, - это однозначно определить значение каждого выражения, а затем проверить, что оно работает в его контексте. Мы не хотим идти в другую сторону и говорить "хорошо, это выражение неоднозначно, поэтому позвольте мне использовать контекст как ключ".
Более конкретно: сначала компилятор должен определить значение a
, а затем a.Value
, а затем определить, является ли назначение законным. Компилятор не говорит "хорошо, я не мог понять, какой из двух свойств a.Value
означает, потому что он неоднозначен, но я собираюсь запутать себя, делая вид, что я это понял, и возвращаюсь и исправляю вещи когда я понимаю, что я на стороне ценности задания, и только одна из этих вещей имеет значение". Также компилятор не говорит: "Я собираюсь использовать один алгоритм поиска, когда я нахожусь слева от задания, а другой, когда я нахожусь справа".
(Помимо этого, конечно, мы не имеем технически говорящего в задании: мы находимся в инициализаторе неявно типизированного локального, который не классифицируется как использование оператора присваивания, но он логически эквивалентен такому, поэтому мы позволим этому пройти без дальнейших комментариев.)
Есть некоторые исключения из этого общего правила, которые направлены на конкретные общие ситуации. Компилятор знает, например, что в выражении формы a.B()
, что B
должно быть что-то invokable; алгоритм поиска элемента автоматически отклоняет не вызывающие вызов элементы без ошибки. Лямбдас, конечно же, категорически отвергает этот принцип; значение лямбда полностью определяется его контекстом. Выполнение этой работы заняло огромную работу - это была одна из моих функций для С# 3 - и мы сделали большие инвестиции, чтобы гарантировать, что алгоритмы являются первичными в общих сценариях. Каждый раз, когда вы рассуждаете извне внутри и снаружи, вы оказываетесь в потенциально экспоненциальных ситуациях, когда вы должны сделать все возможное связывание в процессе, а затем выбрать уникальный, который работает. Эта стоимость стоит того, чтобы отличная функция, такая как тип-выводящая лямбда. Создание других форм работы с чувствительностью к контексту, особенно для неясных сценариев, как тот, который вы описываете, не является хорошим способом потратить ограниченный бюджет.
Итак, пример кода в моем ответе работает, потому что он "объединяет" два определения свойств в один вызов (устраняя неоднозначность компилятора) при выполнении двух интерфейсных контрактов как для геттера, так и для сеттера? Роберт Харви
Чтобы уточнить, код Роберта из его удаленного ответа:
public class GetSet : ISet, IGet
{
public string Value { get; set; }
}
...
getSet.Value = "This is a test";
Debug.Print(getSet.Value); //Prints "This is a test"
Роберт, я не уверен, что понимаю ваш вопрос. Ваш код работает, потому что сначала выполняются контракты для ISet
и IGet
. Класс GetSet
имеет все члены, необходимые каждому, и однозначное отображение. И второе, потому что ваш сайт вызова вообще не использует интерфейсы; он просто вызывает членов класса напрямую. Почему бы не работать?
Теперь, чтобы указать точку в удаленном ответе:
Просто наличие другого интерфейса, который наследует исходные два, не будет работать, потому что нет привязки для привязки.
Нет, это не правильный анализ. Это не имеет никакого отношения к тому, действительно ли свойство реализовано как поле, генерируемое компилятором, или нет. Помните, что свойства на интерфейсах - это просто причудливые способы определения методов get_Value
и set_Value
. Пока свойство с требуемым методом существует в классе реализации, выполняется требование интерфейса. Как это свойство реализовано до класса.
свойство одного класса выполняет интерфейс двух разных контрактов.
Да! Это не проблема. Пока сопоставление элемента интерфейса с классом/структурой может быть однозначно определено, оно отлично. Например:
interface IFoo
{
void M();
}
interface IBar
{
void M();
}
class C : IFoo, IBar
{
public void M() { }
}
M
может выполнять двойную функцию как IFoo.M
, так и IBar.M
.
Где вы попадаете в неприятности, когда нелегко определить, какой метод соответствует интерфейсу. Более подробную информацию см. В моей статье на эту тему:
http://blogs.msdn.com/b/ericlippert/archive/2006/04/05/odious-ambiguous-overloads-part-one.aspx
http://blogs.msdn.com/b/ericlippert/archive/2006/04/06/odious-ambiguous-overloads-part-two.aspx
И для некоторых интересных родственных махинаций см. этот вопрос и ответы, на которые мы с Лучианом обратились: