Только для чтения ("const" -подобные) параметры функции C # - PullRequest
17 голосов
/ 30 сентября 2010

Исходя из фона C ++, я привык вставлять ключевое слово const в определения функций, чтобы объекты передавались в значениях только для чтения. Тем не менее, я обнаружил, что это невозможно в C # (пожалуйста, исправьте меня, если я ошибаюсь). После некоторого поиска в Google я пришел к выводу, что единственный способ создать объект, доступный только для чтения, - это написать интерфейс, который имеет только свойства 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 или создания параметров функции только для чтения без добавления другого свойства или функции?

Ответы [ 3 ]

8 голосов
/ 30 сентября 2010

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

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!
}

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

Кстати, вы можете добиться немного лучшей проверки во время компиляции, если объявите тип свойства в классе как 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; }
    }
}

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

3 голосов
/ 30 сентября 2010

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

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

2 голосов
/ 30 сентября 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...