2010-11-22 4 views
27
class TestClass 
{ 
    private string _privateString = "hello"; 
    void ChangeData() 
    { 
     TestClass otherTestClass = new TestClass(); 
     otherTestClass._privateString = "world"; 
    } 
} 

Этот код компилируется в C#, и эквивалент работает на PHP, но может ли кто-нибудь объяснить причину, по которой здесь можно изменить otherTestClass._privateString?Почему переменная частного члена может быть заменена экземпляром класса?

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

Это не так, поэтому почему создание экземпляра объекта внутри его собственного класса позволяет вам получить доступ к закрытым членам? И должно ли это, разве это не разрывает инкапсуляцию до такой степени? Или я пропущу что-то очевидное?

  • (я не прошу, если выше класс дизайн является хорошей практикой, просто интересно о теории позади него.)

Редактировать - Спасибо за ответы и комментарии. Чтобы уточнить, мне также интересно узнать, может ли быть способным сделать это, как положительную функцию, или если это необходимый компромисс для лучшей проверки времени компиляции/ясности кода/потому что большинство других языков делают это так или иначе. Мне кажется, что в идеале компилятор предупредил бы вас об этом, но тогда я далек от разработчика языка. Любые примеры того, как именно это позволяет вам сделать что-то полезное (без нарушения инкапсуляции), которое в противном случае было бы трудным или невозможным, было бы здорово.

+0

BTW: Вы можете сделать 'ChangeData()' even 'static', и вы все равно сможете получить доступ к закрытым членам. – ulrichb

+0

@ulrichb - Важно отметить для OP, что вы не можете использовать ключевое слово 'this' для доступа к закрытым членам, если' ChangeData() 'является' static'. – TheCloudlessSky

+1

В качестве примера языка, который отличается от - в Ruby, общедоступность - это одно и то же, но частные средства «доступны только для одного и того же экземпляра» и защищенные средства «доступны только из одного класса». Наследование здесь не имеет никакого отношения. – Tesserex

ответ

35

Частные члены доступны для любого кода в тексте программы этого класса (в том числе внутри вложенных типов). Это не имеет никакого отношения к тому экземпляру класса, с которым вы имеете дело.

Я не считаю, что это нарушает инкапсуляцию - API все еще отделен от реализации, но реализация «знает» о себе независимо от того, на каком экземпляре он смотрит.

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

+3

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

+1

@James: Ну, не обязательно.Вы * можете * потребовать, чтобы частные члены были доступны только через эту «ссылку». Это было бы отвратительно, но возможно. –

+1

Я бы добавил к ответу Джона, что этот тип контроля доступа позволяет использовать n-арные операторы, такие как равенство, которое легко записать. Было бы намного сложнее реализовать оператор равенства, если бы ни один экземпляр не имел доступа к другим частным членам других. –

9

Это связано с тем, что C# обеспечивает конфиденциальность на уровне класса, а не на уровне объектов.

Большинство основных языков применяют ту же политику, то есть C#, C++ и Java. Я думаю, что причина такова:

1) потому что разработчики привыкли к такой политике;

2) поскольку неприкосновенность на уровне объектов станет слишком утомительной в обмен на очень мало преимуществ.

+0

Я не верю, что нужно использовать «это», поскольку компиляторы действительно могут различать переменные-члены. Я думаю, было бы утомительно, если бы у вас были методы getter для значения класса-члена, например. Фактически, это было бы против инкапсуляции, потому что вы будете подвергать частные члены другому классу! – Simone

+0

@ miket2e Попробуйте реализовать конструктор копирования или простую функцию Equals с конфиденциальностью на уровне объекта. Это просто в текущем C#: return this._somePrivateField.Eq uals (other._somePrivateField) - если вы можете получить доступ к other._somePrivateField, как бы вы реализовали такую ​​функцию без добавления общих геттеров? –

+1

@MichaelStum: Одно из преимуществ наличия полей private-instance или instance-protected заключается в том, что это делает очевидным, что производные классы могут повторно использовать эти поля, не нарушая Принцип замещения Лискова. В противном случае такое повторное назначение может привести к нарушениям LSP. Что-то вроде 'Equals' может быть реализовано с помощью класса-private' Equals', который принимает содержимое другого объекта (либо в виде кучи дискретных параметров, либо с использованием типа, защищенного классом), и с публичным 'Equals 'метод объекта передает свое состояние методу' Protected' equals другого. – supercat