Неизменяемые ссылочные типы только для чтения и нарушение FXCop: не объявляйте изменяемые ссылочные типы только для чтения - PullRequest
7 голосов
/ 16 февраля 2010

Я пытался обернуть голову вокруг этого нарушения FXCop "DoNotDeclareReadOnlyMutableReferenceTypes"

MSDN: http://msdn.microsoft.com/en-us/library/ms182302%28VS.80%29.aspx

Код из MSDN, который вызвал бы это нарушение:

namespace SecurityLibrary
{
    public class MutableReferenceTypes
    {
        static protected readonly StringBuilder SomeStringBuilder;

        static MutableReferenceTypes()
        {
            SomeStringBuilder = new StringBuilder();
        }
    }
}

Из ответа Джона здесь и здесь я понимаю, что поле, содержащее ссылку на объект (в данном случае SomeStringBuilder), доступно только для чтения, а не сам объект (который создается new StringBuilder())

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

Ответы [ 5 ]

6 голосов
/ 16 февраля 2010

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

Официальная позиция FXCop заключается в том, что она рекомендует объявлять только типы, которые нельзя изменить readonly. Поэтому что-то вроде string хорошо, потому что значение объекта не может быть изменено. Однако значение StringBuilder может быть изменено, но если сделать его доступным только для чтения, вы не сможете назначить поле другому экземпляру StringBuilder или null после запуска конструктора.

Я не согласен с FXCop по этому правилу. Пока кто-то понимает, что это просто принуждение о том, что ссылка не может изменить пост-конструкцию, нет никакой путаницы.

Обратите внимание, что типы значений становятся неизменяемыми с помощью ключевого слова readonly, а ссылочные типы - нет.

namespace SecurityLibrary
{
    public class MutableReferenceTypes
    {
        static protected readonly StringBuilder SomeStringBuilder;

        static MutableReferenceTypes()
        {
            // allowed
            SomeStringBuilder = new StringBuilder();
        }

        void Foo()
        {
            // not allowed
            SomeStringBuilder = new StringBuilder();
        }

        void Bar()
        {
            // allowed but FXCop doesn't like this
            SomeStringBuilder.AppendLine("Bar");
        }
    }
}
3 голосов
/ 16 февраля 2010

Так как класс MutableReferenceTypes представлен в вопросе, вы не можете действительно изменить его от любого внешнего вызывающего, поскольку поле SomeStringBuilder является закрытым.

Однако сам класс может изменять поле. Это не в настоящее время, но это может произойти в более поздней итерации.

Вот пример метода:

public static void Mutate()
{
    SomeStringBuilder.AppendLine("Foo");
}

Вызов метода Mutate приведет к изменению класса, потому что SomeStringBuilder теперь изменится.

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

0 голосов
/ 16 февраля 2010

Вы бы не изменили стоимость объекта. В этом смысл правила. Любое поле, объявленное только для чтения, должно быть таковым только для чтения. Наличие только для чтения изменяемой ссылки - оксюморон. Если вы можете изменить значение того, на что «указывает» поле, тогда оно больше не будет доступно только для чтения. В действительности нет никакой функциональной разницы между назначением значения всех членов некоторого объекта A некоторому объекту B, представленному полем, или простым присвоением A этому полю (когда они одного типа), однако только один из них действителен, когда поле объявляется только для чтения, но поскольку вы можете эффективно изменять значение того, что представлено полем, оно, как уже было сказано, не совсем доступно для чтения

0 голосов
/ 16 февраля 2010

Вы не можете изменить ссылку, но любой вызов (изменяемого) объекта меняет его состояние.

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

По сути, readonly никоим образом не гарантирует, что объект не меняется, просто говорит, что ссылка не меняется.

0 голосов
/ 16 февраля 2010

.Net имеет список разрешенных типов неизменяемых ссылок, StringBuilder не является одним из них.

Жалоба на то, что то, что вы создаете, не является неизменным, хотя статический конструктор вызывается один раз, а класс инициализируется один раз, и все это остается неизменным, а остальное изменчиво. Один поток может вызвать .Append(), затем другой ... вы видите, как сам строитель строк мутирует, а на самом деле не readonly, потому что он постоянно меняет состояния / мутирует.

Объявление его readonly действительно неверно, поскольку объект, на который есть ссылка, сам по себе постоянно меняется.

...