Публичная переменная С# как доступная для записи внутри класса, но только для чтения вне класса

У меня есть класс .Net С#, где мне нужно сделать переменную общедоступной. Мне нужно инициализировать эту переменную в методе (не в конструкторе). Однако я не хочу, чтобы переменная могла быть изменена другими классами. Это возможно?


person MikeTWebb    schedule 11.01.2011    source источник
comment
А награда за самый быстрый вопрос на 5 ответов достается...   -  person 3Dave    schedule 11.01.2011


Ответы (8)


Не используйте поле — используйте свойство:

class Foo
{
    public string Bar { get; private set; }
}

В этом примере Foo.Bar доступен для чтения везде и доступен для записи только членам самого Foo.

В качестве примечания: в этом примере используется функция C#, представленная в версии 3, которая называется автоматически реализуемые свойства. Это синтаксический сахар, который компилятор преобразует в обычное свойство с закрытым резервным полем, например:

class Foo
{
    [CompilerGenerated]
    private string <Bar>k__BackingField;

    public string Bar
    {
        [CompilerGenerated]
        get
        {
            return this.<Bar>k__BackingField;
        }
        [CompilerGenerated]
        private set
        {
            this.<Bar>k__BackingField = value;
        }
    }
}
person Andrew Hare    schedule 11.01.2011
comment
Но что, если это ссылочный тип, а не тип значения? Не может ли другой класс прочитать ссылку, а затем изменить свойства объекта, на который она указывает? - person JoeMjr2; 14.03.2016
comment
Они могут. Например, даже в этом случае можно вызвать список Add, если список является членом. Мне пришлось иметь дело с этим со списком (конечно, это только один вариант использования ссылки), и в итоге я сделал член ReadOnlyCollection только с геттером и частной переменной - списком. - person Do-do-new; 05.11.2018

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

public string SomeProperty { get; private set; }

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

person cdhowie    schedule 11.01.2011

Конечно. Сделайте его свойством и сделайте сеттер закрытым:

public Int32 SomeVariable { get; private set; }

Затем, чтобы установить его (из какого-то метода в классе):

SomeVariable = 5;
person Chris Shain    schedule 11.01.2011

Используйте частную переменную и выставляйте общедоступное свойство.

class Person
{
  private string name;

  public string Name
  {
    get
    {
      return name;
    }
  }
}
person Henrik    schedule 11.01.2011

Разве вам не разрешено использовать недвижимость для этого? Если ты:

private string _variable
public string Variable {
    get {
        return _variable;
    }
}
person Anders Arpi    schedule 11.01.2011

Пока ответы работают хорошо, если вы не используете ссылочные типы. В противном случае вы все равно сможете манипулировать внутренностями этой переменной. например:

using System;
namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var fo = new Fo();
            fo.Init();
            Console.WriteLine(fo.SomeBar.SomeValue);
            fo.SomeBar.SomeValue = "Changed it!";
            Console.WriteLine(fo.SomeBar.SomeValue);
            Console.Read();
        }
        public class Fo
        {
            public Bar SomeBar { get; private set; }
            public void Init()
            {
                SomeBar = new Bar{SomeValue = "Hello World!"};
            }
        }
        public class Bar
        {
            public String SomeValue { get; set; }
        }
    }
}
Это приведет к выводу консоли:

Hello World!
Changed it!

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


using System;
namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var fo = new Fo();
            fo.Init();
            Console.WriteLine(fo.SomeBar.SomeValue);
            fo.SomeBar.SomeValue = "Changed it!";
            Console.WriteLine(fo.SomeBar.SomeValue);
            Console.Read();
        }
        public class Fo
        {
            private Bar _someHiddenBar;
            public Bar SomeBar => new Bar(_someHiddenBar);
            public void Init()
            {
                _someHiddenBar = new Bar{SomeValue = "Hello World!"};
            }
        }
        public class Bar
        {
            public String SomeValue { get; set; }
            public Bar(){}
            public Bar(Bar previousBar)
            {
                SomeValue = previousBar.SomeValue;
            }
        }
    }
}
которая будет результат на выходе:

Hello World!
Hello World!

Смотрите комментарии, почему я добавил третий пример:


using System;
namespace Playground
{
    class Program
    {
        static void Main(string[] args)
        {
            var fo = new Fo();
            fo.Init();
            Console.WriteLine(fo.SomeBar.SomeValue);
            //compile error
            fo.SomeBar.SomeValue = "Changed it!";
            Console.WriteLine(fo.SomeBar.SomeValue);
            Console.Read();
        }
        public class Fo
        {
            private Bar _someHiddenBar;
            public Bar SomeBar => new Bar(_someHiddenBar);
            public void Init()
            {
                _someHiddenBar = new Bar("Hello World!");
            }
        }
        public class Bar
        {
            public String SomeValue { get; }
            public Bar(string someValue)
            {
                SomeValue = someValue;
            }
            public Bar(Bar previousBar)
            {
                SomeValue = previousBar.SomeValue;
            }
        }
    }
}

person kkCosmo    schedule 08.08.2017
comment
Это не выглядит хорошо. Вы вызываете fo.SomeBar.SomeValue = "Changed it!";, который не выполняет ни присваивания, ни исключения. Если бы это был настоящий код, его отладка была бы кошмаром. Вы должны сделать вещи неизменяемыми, чтобы вы даже не могли скомпилировать или, вы должны генерировать исключение. - person rbm; 08.08.2017
comment
Это хороший момент. Как бы вы изменили его, чтобы SomeValue по-прежнему мог быть изменен его владельцем, но не по ссылке? - person kkCosmo; 08.08.2017
comment
Посмотрите на другие ответы, private set; - наиболее распространенный способ. В вашем примере фактически два класса, поэтому вам нужно будет контролировать Fo.Bar и Bar.SomeValue и сделать их неизменяемыми. - person rbm; 08.08.2017
comment
Но это не относится к делу. JoeMjr2 указал (в комментарии к верхнему ответу), что это работает для типов значений, но вы все равно можете изменять переменные ссылочного типа. Отсюда мой пример с двумя классами. Если сделать SomeValue закрытым, то Fo модификация, о которой просил MikeTWebb, не будет разрешена. - person kkCosmo; 08.08.2017
comment
Нет, если вы сделаете класс неизменяемым, т.е. установите значения в конструкторе. - person rbm; 08.08.2017
comment
В зависимости от потребностей «Fo» и сложности «Bar» это имеет свои преимущества. Я согласен. В этом случае конструктор «Bar» примет значение для «SomeValue», а «SomeValue» будет определен, например, с помощью частный сеттер. Я добавлю его в качестве третьего примера. - person kkCosmo; 08.08.2017

Некро точно, но это стоит упомянуть с улучшениями языка в 6.0

class Foo {

    // The new assignment constructor is wonderful shorthand ensuring
    // that the var is only writable inside the obj's constructor
    public string Bar { get; private set; } = String.Empty;

    }
person MisterNad    schedule 09.06.2016
comment
Это не будет работать внутри метода, потому что это свойство будет доступно только для чтения. - person usefulBee; 23.05.2017

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

person Rami Alshareef    schedule 11.01.2011