Подтвердить что ты не робот

Только для чтения ( "const" -подобные) параметры функции С#

Исходя из фона С++, я использую ключевое слово const в определениях функций, чтобы объекты передавались в значениях только для чтения. Тем не менее, я выяснил, что это невозможно в С# (пожалуйста, поправьте меня, если я ошибаюсь). После некоторого Googling я пришел к выводу, что единственный способ сделать объект только для чтения - написать интерфейс, который имеет только свойства "get" и передать это вместо. Должен сказать, элегантный.

public interface IFoo
{
  IMyValInterface MyVal{ get; }
}

public class Foo : IFoo
{
  private ConcreteMyVal _myVal;

  public IMyValInterface MyVal
  {
    get { return _myVal; }
  }
}

Я передал бы это:

public void SomeFunction(IFoo fooVar)
{
  // Cannot modify fooVar, Excellent!!
}

Это нормально. Однако в остальной части моего кода я хотел бы изменить свой объект в обычном режиме. Добавление свойства 'set' к интерфейсу приведет к нарушению моего ограничения только для чтения. Я могу добавить свойство 'set' к Foo (а не IFoo), но подпись ожидает интерфейс, а не конкретный объект. Я должен был бы сделать некоторые кастинга.

// Add this to class Foo. Might assign null if cast fails??
set { _myVal = value as ConcreteMyVal; }

// Somewhere else in the code...
IFoo myFoo = new Foo;
(myFoo as Foo).MyFoo = new ConcreteMyVal();

Есть ли более элегантный способ репликации const или создания параметров функции только для чтения без добавления другого свойства или функции?

4b9b3361

Ответ 1

Я думаю, вы можете искать решение с двумя интерфейсами, в которых один наследуется от другого:

public interface IReadableFoo
{
    IMyValInterface MyVal { get; }
}

public interface IWritableFoo : IReadableFoo
{
    IMyValInterface MyVal { set; }
}

public class Foo : IWritableFoo 
{
    private ConcreteMyVal _myVal;

    public IMyValInterface MyVal
    {
        get { return _myVal; }
        set { _myVal = value as ConcreteMyVal; }
    }
}

Затем вы можете объявить методы, тип параметра которых "сообщает", планирует ли он изменить переменную или нет:

public void SomeFunction(IReadableFoo fooVar)
{
    // Cannot modify fooVar, excellent!
}

public void SomeOtherFunction(IWritableFoo fooVar)
{
    // Can modify fooVar, take care!
}

Это имитирует проверки времени компиляции, подобные константе в С++. Как правильно указал Эрик Липперт, это не то же самое, что неизменность. Но, как программист на С++, я думаю, вы это знаете.

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

public class Foo : IWritableFoo 
{
    private ConcreteMyVal _myVal;

    public ConcreteMyVal MyVal
    {
        get { return _myVal; }
        set { _myVal = value; }
    }

    public IMyValInterface IReadableFoo.MyVal { get { return MyVal; } }
    public IMyValInterface IWritableFoo.MyVal
    {
        // (or use "(ConcreteMyVal)value" if you want it to throw
        set { MyVal = value as ConcreteMyVal; }
    }
}

Таким образом, сеттер может выходить только при доступе через интерфейс, но не при доступе через класс.

Ответ 2

Прежде всего, вы правы: вы не можете применять константу или аналогичное ключевое слово к параметрам в С#.

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

Ответ 3

Рассмотрим сначала ответ Тимви. Но в качестве второго варианта вы можете сделать это, сделав его более похожим на ключевое слово C CONST.

Параметры ссылочного типа (объекта) по умолчанию являются параметрами IN. Но поскольку они являются ссылками, их побочные эффекты и доступ к свойствам выполняются с объектом вне метода. Объект не должен быть потерян. Он по-прежнему был изменен методом.

Однако параметр value-type (struct) также является IN по умолчанию и не может иметь побочных эффектов или модификаций свойств для элемента, который был передан. Вместо этого он переходит к COPIED ON WRITE перед тем, как перейти в метод. Любые изменения в нем внутри этого метода умирают, когда метод выходит за рамки (конец метода).

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

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