“Правильність const” у C #


79

Сенс коректності полягає у можливості забезпечити подання екземпляра, який не може бути змінений або видалений користувачем. Компілятор підтримує це, вказуючи, коли ви порушуєте constness із функції const або намагаєтесь використовувати функцію non-const об'єкта const. Тож, не копіюючи підхід const, чи існує методологія, яку я можу використовувати в C #, яка має однакові цілі?

Я знаю про незмінність, але це насправді не переноситься на об'єкти контейнера, щоб назвати їх, а лише один приклад.


Існує приємна дискусія щодо можливих дизайнів лише для читання за адресою http://blogs.msdn.com/brada/archive/2004/02/04/67859.aspx
Asaf R,

Тоді як щодо реалізації const_castв C #?
kerem

Відповіді:


64

Я теж неодноразово стикався з цим питанням і в кінцевому підсумку використовував інтерфейси.

Я думаю, що важливо відмовитись від думки, що C # - це будь-яка форма або навіть еволюція C ++. Це дві різні мови, які мають майже однаковий синтаксис.

Я зазвичай висловлюю "правильність const" на C #, визначаючи подання класу лише для читання:

public interface IReadOnlyCustomer
{
    String Name { get; }
    int Age { get; }
}

public class Customer : IReadOnlyCustomer
{
    private string m_name;
    private int m_age;

    public string Name
    {
        get { return m_name; }
        set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        set { m_age = value; }
    }
}

12
Звичайно, але що, якщо одне з ваших полів - це Список або розширений тип. Ваше рішення дуже швидко ускладнюється.
Matt Cruikshank,

12
@Trap: у цьому вся суть питання. Повернення внутрішньої колекції є поганою практикою, оскільки зовнішній світ може модифікувати ваші внутрішні елементи, в C ++, що вирішується за допомогою const: ви надаєте постійний перегляд колекції (не можете додавати / видаляти елементи, лише пропонуєте постійний погляд на її елементи) і як такий є безпечним (і загальною ідіомою). Не потрібно розробляти інтерфейси або інші трюки, щоб уникнути зміни зовнішнього коду зовнішнім кодом.
Девід Родрігес - dribeas

9
@Trap, я згоден, що механізм є допоміжним засобом для розробки, який включає виявлення помилок розробників, досвідчених чи ні. Я не згоден з тим, що писати інтерфейси, призначені лише для читання, є кращим рішенням з кількох підстав. Перший момент полягає в тому, що кожного разу, коли ви пишете клас на C ++, ви неявно визначаєте константний інтерфейс: заявлений підмножина членів const, без зайвих витрат на визначення окремого інтерфейсу і вимагає відправлення під час виконання. Тобто, мова забезпечує простий спосіб реалізації const-інтерфейсів під час компіляції.
Девід Родрігес - dribeas

10
Важливо зазначити, що const-ness є частиною дизайну, і він декларує намір так само чітко, як і написання зовнішнього const-інтерфейсу. Крім того, надання const-інтерфейсів може бути складною задачею, якщо з нею доводиться вирішуватись вручну, і може знадобитися написання майже стільки інтерфейсів, скільки класів є у складеному типі. Це підводить нас до вашого останнього висловлювання: "скрутити речі через те, що забули додати const". Легше звикнути додавати constвсюди (вартість розробки невелика), ніж писати інтерфейси лише для читання для кожного класу.
Девід Родрігес - dribeas

9
Однією з причин, що мені дуже сподобався C #, коли він вийшов, було те, що він не викинув усіх прагматичних особливостей C ++, як це зробила Java. Java намагалася робити все з інтерфейсами, і це було катастрофою. Доказ - у пудингу. З часом в Java було додано багато функцій, які спочатку засуджувались як єресь. Дизайн на основі інтерфейсу має своє місце, але якщо дойти до крайнощів, це може зайво ускладнити архітектуру програми. У підсумку ви витрачаєте більше часу на написання зразкового коду і менше часу на корисні справи.
kgriffs

29

Щоб скористатися перевагами безумства (або чистоти з точки зору функціонального програмування), вам потрібно буде сконструювати свої класи таким чином, щоб вони були незмінними, як і клас String з c #.

Цей підхід набагато кращий, ніж просто позначення об'єкта як лише для читання, оскільки за допомогою незмінних класів ви можете легко передавати дані в багатозадачних середовищах.


8
але незмінність насправді не масштабується до складних об'єктів, чи ні?
tenpn

8
Я стверджую, що якби ваш об'єкт був настільки складним, що незмінність була неможливою, ви мали б хорошого кандидата на рефакторинг.
Jim Burger

2
Я думаю, що цей найкращий з групи. Незмінні об'єкти використовуються занадто рідко.

3
Мало того, але бувають випадки, коли ви хочете мати об’єкти, що змінюються (об’єкти змінюються!), Але в більшості випадків все одно пропонують перегляд лише для читання. Незмінні об'єкти означають, що кожного разу, коли вам потрібно внести зміни (зміни трапляються), вам потрібно буде зробити новий об'єкт із усіма тими ж даними, крім зміни. Подумайте про школу, яка має шкільні кімнати, учнів ... Ви хочете створити нову школу кожного разу, коли проходить дата народження учня та її вік змінюється? Або ви можете просто змінити вік на рівні учнів, студент на рівні кімнати, можливо, кімната на рівні школи?
Девід Родрігес - dribeas

8
@tenpn: Незмінність насправді неймовірно добре масштабується, якщо все правильно зробити. Насправді допомагає використання Builderкласів для великих незмінних типів (Java та .NET визначають StringBuilderклас, що є лише одним із прикладів).
Конрад Рудольф

24

Я просто хотів зазначити для вас, що багато контейнери System.Collections.Generics мають метод AsReadOnly, який поверне вам незмінну колекцію.


4
Це все ще має проблему, що Ts можна змінювати, навіть якщо ReadOnlyCollection <T> немає.
arynaq

4

C # не має такої функції. Ви можете передавати аргумент за значенням або за посиланням. Посилання є незмінним, якщо ви не вказали модифікатор ref . Але дані, на які посилаються, не є незмінними. Тому потрібно бути обережним, якщо хочеш уникнути побічних ефектів.

MSDN:

Передавання параметрів


ну, думаю, саме до цього і зводиться питання: який найкращий спосіб уникнути побічних ефектів відсутності структури const?
tenpn

На жаль, допомогти можуть лише незмінні типи. Ви можете поглянути на Spec # - є кілька цікавих перевірок часу компіляції.
aku

3

Інтерфейси - це відповідь, і вони насправді потужніші за "const" у C ++. const - це універсальне рішення проблеми, де "const" визначається як "не встановлює членів або викликає щось, що встановлює членів". У багатьох сценаріях це хороший стенограф для стійкості, але не у всіх. Наприклад, розглянемо функцію, яка обчислює значення на основі деяких членів, але також кешує результати. У C ++ це вважається неконстантним, хоча з точки зору користувача це, по суті, const.

Інтерфейси дають вам більшу гнучкість у визначенні конкретного підмножини можливостей, які ви хочете надати з вашого класу. Хочете нестабільність? Просто надайте інтерфейс без методів мутації. Хочете дозволити встановлювати деякі речі, але не інші? Надайте інтерфейс саме з цими методами.


15
Не на 100% правильно. Методи const C ++ дозволяють мутувати члени, позначені як mutable.
Константин

3
Досить справедливо. А констинг-лиття дозволяє позбутися від константи. І те, і інше означає, що навіть дизайнери C ++ зрозуміли, що універсальний варіант - це справді універсальний варіант.
чудовий

8
Перевага С ++ полягає в тому, що у вас є const для більшості випадків, і ви можете реалізовувати інтерфейси для інших. Тепер, крім const, C ++ має змінне ключове слово, яке можна застосувати до атрибутів як кеш-пам’яті даних або механізмів блокування (mutexes тощо). Const is 'не змінить жодного внутрішнього атрибута), а навпаки, не змінить стан об'єкта, як сприймається ззовні. Тобто будь-яке використання об’єкта до і після виклику методу const дасть однаковий результат.
Девід Родрігес - dribeas

9
відкидання const для мутації комети в C ++ - це незахищена поведінка (носові демони). const_cast призначений для взаємодії із застарілим кодом, який, будучи логічно const, не позначений як const.
jk.

3

Погодьтеся, що деякі інші розглядають використання полів лише для читання, які ви ініціалізуєте в конструкторі, для створення незмінних об'єктів.

    public class Customer
    {
    private readonly string m_name;
    private readonly int m_age;

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
    }

    public int Age
    {
        get { return m_age; }
    }
  }

Ви також можете додати область доступу до властивостей, тобто загальнодоступний і захищений набір?

    public class Customer
    {
    private string m_name;
    private int m_age;

    protected Customer() 
    {}

    public Customer(string name, int age)
    {
        m_name = name;
        m_age = age;
    }

    public string Name
    {
        get { return m_name; }
        protected set { m_name = value; }
    }

    public int Age
    {
        get { return m_age; }
        protected set { m_age = value; }
    }
  }

3
Це різні підходи, які насправді не відповідають цілій проблемі. З рівнем контролю доступу ви дозволяєте лише виводити класи із змін, у багатьох випадках це не зможе адекватно моделювати реальний світ. Вчитель не бере висновок із записів студентів, але може захотіти змінити оцінки студента, хоча студент не може змінити оцінки, але може їх прочитати ... лише для наведення простого прикладу.
Девід Родрігес - dribeas

2
  • СопзЬ ключове слово можна використовувати для компіляції констант часу , таких як примітивні типів і рядки
  • Тільки для читання ключового слова може бути використано для констант часу виконання , таких як посилальні типи

Проблема лише з читанням полягає в тому, що він дозволяє лише посилання (вказівник) бути постійним. Реч, на яку посилається (на яку вказують), все ще може бути змінена. Це складна частина, але її ніяк не обійти. Реалізувати постійні об'єкти означає змусити їх не піддаватись змінним методам чи властивостям, але це незручно.

Див. Також Ефективний C #: 50 конкретних способів покращити ваш C # (Пункт 2 - Віддавайте перевагу лише читанню, а не const.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.