Является ли отражение нарушением принципа инкапсуляции?

ладно, допустим, у нас есть класс, определенный как

public class TestClass
{
    private string MyPrivateProperty { get; set; }

    // This is for testing purposes
    public string GetMyProperty()
    {
        return MyPrivateProperty;
    }
}

тогда попробуем:

TestClass t = new TestClass { MyPrivateProperty = "test" };

компиляция завершается с TestClass.MyPrivateProperty is inaccessible due to its protection level, как ожидалось.

попробовать

TestClass t = new TestClass();
t.MyPrivateProperty = "test";

и компиляция снова терпит неудачу, с тем же сообщением.

все хорошо до сих пор, мы ожидали этого.

но потом пишут:

PropertyInfo aProp = t.GetType().GetProperty(
        "MyPrivateProperty",
        BindingFlags.NonPublic | BindingFlags.Instance);

// This works:
aProp.SetValue(t, "test", null);

// Check
Console.WriteLine(t.GetMyProperty());

и вот мы здесь, нам удалось изменить приватное поле.

разве это не ненормально, чтобы иметь возможность изменить внутреннее состояние какого-то объекта, просто используя отражение?

Edit:

Спасибо за ответы до сих пор. Для тех, кто говорит: "вам не нужно использовать его": как насчет дизайнера класса, похоже, он больше не может предполагать внутреннюю безопасность государства?

9 ответов


отражение нарушает принципы инкапсуляции, предоставляя доступ к частным полям и методам, но это не первый или единственный способ обойти инкапсуляцию; можно утверждать, что сериализация предоставляет все внутренние данные класса, информацию, которая обычно была бы частной.

важно понимать, что инкапсуляция-это только метод, который упрощает проектирование поведения, при условии, что потребители согласны использовать API, который вы определили. Если кто-то решив обойти ваш API с помощью отражения или любой другой техники, они больше не имеют гарантии, что ваш объект будет вести себя так, как вы его разработали. Если кто-то присваивает значение null к частному полю, они должны быть готовы поймать NullReferenceException в следующий раз, когда они пытаются использовать свой класс!

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

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


рефлексия-это инструмент. Вы мая используйте его для разрыва инкапсуляции, когда он дает вам больше, чем забирает.

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

с другой стороны, есть некоторые задачи, которые не были бы разрешимы без этого механизма, например, работа с типами генерации во время выполнения (хотя, это будет намного проще, начиная с .NET 4.0 с его DLR и "динамическими" переменными в C# 4.0).


Я полагаю, вы могли бы сказать, что. Можно также сказать, что API-интерфейсы CodeDom, Reflection Emit и Expression Tree нарушают инкапсуляцию, позволяя писать код "на лету". Это просто средства, предоставляемые .Сеть. Вы не должны использовать их.

Не забывайте ,что вы должны (a) работать в полном доверии, и (b) явно указать BindingFlags.NonPublic, для доступа к частным данным. Этого должно быть достаточно.


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

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

если вы думаете, что отражение должно быть невозможно, ты спишь!

в C++ нет отражения как такового. Но есть базовая "объектная модель", которую генерируемый компилятором машинный код использует для доступа к структуре объектов (и виртуальных функций). Таким образом, программист на C++ может точно так же нарушить инкапсуляцию.

class RealClass
{
private:
    int m_secret;
};

class FakeClass
{
public:
    int m_notSecret;
};

мы можем взять указатель на объект типа RealClass и просто бросил его в FakeClass и удалить "закрытый член.

любая ограниченная система должна быть реализована поверх более гибкой системы, поэтому ее всегда можно обойти. Если отражение не было предоставлено как функция BCL, кто-то может добавить его с библиотекой, используя небезопасный код.

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

(function() {

    var x = 5;

    myGetter = function() { return x; };
    mySetter = function(v) { x = v; };

})();

после этого выполняется, глобальное пространство имен содержит две функции,myGetter и mySetter, которые являются единственным способом доступа к значению x. Javascript не имеет рефлексивной способности добраться до x любым другим способом. Но он должен работать в каком-то хост-интерпретаторе (например, в браузере), и поэтому, конечно, есть какой-то ужасный способ манипулировать x. Ля ошибка повреждения памяти в плагине может сделать это случайно!


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

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


Это часть функциональности, предоставляемой Reflection, но, я бы сказал, Не лучшее ее использование.

Он работает только под полным доверием.


принцип инкапсуляции удерживается вашим API, но отражение дает вам возможность работать вокруг API.

тот, кто использует отражение знает, что он не использует ваш API правильно!


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

рефлексия-это инструмент, его можно использовать или злоупотреблять.


Что касается вопроса после редактирования: ответ просто нет. Дизайнер API может сделать все возможное, приложив много усилий для раскрытия чистого интерфейса и увидеть, что его API чрезмерно злоупотребляют с помощью отображение.

инженер может моделировать идеальную стиральную машину, которая безопасна, не шокирует вас электричеством и так далее, но если вы разобьете ее молотком, пока не увидите кабели, никто не обвинит инженера, если вы получите удар :D


отражение может помочь вам сохранить ваш код чистым. Например. при использовании hibernate вы можете напрямую привязать частные переменные к значениям БД, и вам не нужно писать ненужные методы сеттера, которые вам не понадобятся в противном случае. С этой точки зрения отражение может помочь вам сохранить инкапсуляцию.