Переопределение значений по умолчанию в структуре (c #)

#c# #struct #defaults

#c# #структура #значения по умолчанию

Вопрос:

Возможно ли установить или переопределить состояние по умолчанию для структуры?

В качестве примера у меня есть

 enum something{a,b,c,d,e};
  

и структура, которая связывает 2 значения для этого перечисления

 struct SomethingData
{
    something type;
    int Value;
    double Multipler;

    SomethingData(something enumVal, int intVal, double DblVal) {...}
}
  

Но могу ли я указать, что состояние по умолчанию равно

 SomethingData(something.c,0,1);
  

Комментарии:

1. Вы могли бы установить a равным -2 (таким образом, c=0). Или, возможно, даже создать константу по умолчанию (или, скорее, статическую, доступную только для чтения).

Ответ №1:

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

Структуры не могут содержать явных конструкторов без параметров. Элементы структуры автоматически инициализируются значениями по умолчанию. Структура не может иметь инициализатор в виде: base (список аргументов).

http://msdn.microsoft.com/en-us/library/aa288208 (v =против 71).aspx

Итак, короткий ответ, нет, вы не можете переопределить конструктор по умолчанию (у каждой структуры есть конструктор без параметров, и вы не можете скрыть его или переопределить)…

Комментарии:

1. как насчет обходного пути, который позволяет указать, каковы значения по умолчанию? лучшее, что я могу придумать, — это создать постоянный экземпляр структуры в структуре и затем присвоить этому значению значение вместо вызова конструктора по умолчанию

2. @Mike: Ты мог бы это сделать. Но это не скроет и не устранит конструктор типа по умолчанию. (Обратите внимание, что для этого вам придется использовать static readonly поле вместо const , поскольку поля типа struct нельзя сделать постоянными.)

3. Однако это досадно, особенно когда вы объявляете массив некоторой структуры и хотите, чтобы конкретный элемент структуры имел определенное значение по умолчанию … вам нужно перебрать массив и установить его. (Например, я перенес алгоритм из C, который придал особое значение ‘-1’). Но, хотя это больше кода, возможно, в конечном итоге мы выполняем не больше работы , чем если бы нам разрешили указывать альтернативные значения по умолчанию?

4. Как насчет создания статического свойства Default , которое создает ваш объект по умолчанию? Вместо вызова new Foo() вы бы вызвали Foo.Default . Это удерживает значения по умолчанию в центральном месте и обеспечивает минимальное количество дополнительного кода.

Ответ №2:

Вы не можете. Структуры всегда имеют конструктор по умолчанию, который присваивает каждому элементу значение по умолчанию ( null для ссылочных типов, 0 для числовых типов, false для bools и т.д.) Это поведение не может быть изменено.

Комментарии:

1. Как насчет перечислений?

2. Редактировать, только что проверил. По умолчанию перечисляет первый индекс перечисления. На самом деле это чрезвычайно мощный метод, особенно если первый индекс объявлен как «None».

3. @Krythic На самом деле они будут использовать по умолчанию любой элемент перечисления, имеющий значение 0, которое действительно будет первым, если вы явно не назначаете значения. Но если вы сделаете что-то подобное, enum Foo { A = 1, B = 0 } тогда вы обнаружите, что default(Foo) == Foo.B вместо этого. Если в вашем перечислении нет элемента со значением 0, то значение по умолчанию вообще не будет одним из ваших параметров перечисления.

Ответ №3:

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

http://csharp.2000things.com/2010/10/03/108-defining-a-constructor-for-a-struct/

Ответ №4:

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

Когда создается хранилище типа класса, оно изначально будет содержать нулевую ссылку. Когда создается хранилище структурного типа, все его поля (и любые поля структур внутри него) будут работать одновременно. В отличие от экземпляров объектов класса, которые могут появиться только с помощью конструктора, хранилища структурного типа создаются без использования какого-либо собственного кода структуры. Следовательно, определение структуры не будет влиять на то, что должно произойти, когда появятся «экземпляры» [т. Е. хранилища структурного типа].

Структура — это, по сути, набор полей, связанных между собой клейкой лентой. Если предполагается, что структура ведет себя как что-то другое, обычно ей следует делать свои поля закрытыми и притворяться неизменяемыми [даже если назначение структуры фактически изменяет структуру назначения, перезаписывая все ее поля соответствующими значениями из источника, и определение структуры не имеет права голоса в этом вопросе]. Однако, если предполагается, что структура инкапсулирует фиксированный набор связанных, но независимых значений (например, координаты точки), которые могут независимо принимать любую комбинацию значений, разрешенных для их соответствующих типов, структура должна просто предоставлять свои поля публично. Некоторые люди могут ныть о том, что «изменяемые структуры — это зло», но зло применяется только при вызове самоизменяющихся методов в структуре. Структуры, которые отображают свое состояние в виде полей, ведут себя как наборы переменных, склеенных клейкой лентой. Если кому-то нужен набор переменных, склеенных клейкой лентой, попытка заставить структуру притворяться неизменяемой просто усложнит программирование.

Ответ №5:

Существует обходной путь, позволяющий сделать это с помощью пользовательских средств получения свойств. Наблюдать:

 public struct Foostruct
{
    private int? _x;
    private int? _y;

    public int X
    {
        get { return _x ?? 20; } // replace 20 with desired default value
        set { _x = value; }
    }

    public int Y
    {
        get { return _y ?? 10; } // replace 10 with desired default value
        set { _y = value; }
    }
}
  

Это будет работать только для типов значений (которые могут быть обернуты с помощью nullable), но потенциально вы могли бы сделать что-то подобное для ссылочных типов, обернув их в универсальный класс, как показано ниже:

 public class Wrapper<TValue>
{
    public TValue Value { get; set; }
}

public struct Foostruct
{
    private Wrapper<Tick> _tick;

    public Tick Tick
    {
        get { return _tick == null ? new Tick(20) : _tick.Value; }
        set { _tick = new Wrapper<Tick> { Value = value }; }
    }
}
  

Ответ №6:

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

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

 struct ImmutableValueType
{
    private int _ID;
    private string _Name;

    public int ID
    {
        get { return _ID; }
    }

    public string Name
    {
        get { return _Name; }
    }

    // Infuser struct defined within the ImmutableValueType struct so that it has access to private fields
    public struct Infuser
    {
        private ImmutableValueType _Item;

        // write-only properties provide the complement to the read-only properties of the immutable value type
        public int ID
        {
            set { _Item._ID = value; }
        }

        public string Name
        {
            set { _Item._Name = value; }
        }

        public ImmutableValueType Produce()
        {
            return this._Item;
        }

        public void Reset(ImmutableValueType item)
        {
            this._Item = item;
        }

        public void Reset()
        {
            this._Item = new ImmutableValueType();
        }

        public static implicit operator ImmutableValueType(Infuser infuser)
        {
            return infuser.Produce();
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        // use of object initializer syntax made possible by the Infuser type
        var item = new ImmutableValueType.Infuser
        {
            ID = 123,
            Name = "ABC",
        }.Produce();

        Console.WriteLine("ID={0}, Name={1}", item.ID, item.Name);
    }
}
  

Комментарии:

1. Не делает ли это ImmutableValueType изменяемым типом? (поскольку это может быть изменено с помощью типа Infuser) Я думаю, для этого и существуют конструкторы

2. Может быть грязно / утомительно писать несколько перегрузок конструктора, чтобы учесть все возможные способы инициализации объекта. Что касается неизменности: как я уже упоминал, на мой взгляд, неизменность сохраняется. Вы не можете получить ссылку на «неизменяемый» объект без вызова метода Produce infuser’а (хорошо, я добавил неявный оператор преобразования, так что вам решать, удалять ли его). Как только значение было возвращено из метода infuser’s Produce, пользователь infuser больше не может манипулировать этим значением, поскольку в этом случае это тип значения.

Ответ №7:

Каждый раз, когда вы получаете / устанавливаете свойство, вам нужно установить значение по умолчанию, вызывая метод InitDefaultValues()

 private string _numberDecimalSeparator;
public string NumberDecimalSeparator
{
    get
    {
        InitDefaultValues();
        return _numberDecimalSeparator;
    }
    set
    {
        InitDefaultValues(); 
        _numberDecimalSeparator = value;
    }
}
  

 private void InitDefaultValues()
{
    if (!_inited)
    {
        _inited = false;
        var ci = CultureInfo.CurrentCulture;
         _numberDecimalSeparator = ci.With(x => x.NumberFormat).Return(x => x.NumberDecimalSeparator, ".");

        ...
    }
}
  

Комментарии:

1. Структуры не должны видоизменяться в средствах получения свойств. Если MyObject.Foo есть поле, базовый тип которого является типом вашей структуры, то при чтении MyObject.Foo.NumberDecimalSeparator будет учитываться текущий язык; если это свойство такого типа, то при чтении MyObject.Foo.NumberDecimalSeparator будет создана временная копия базовой структуры, в эту копию будет перенесен текущий язык, вычислено возвращаемое значение NumberDecimalSeparator и отменена временная структура.

Ответ №8:

Довольно глупо, но работает

 public readonly static float default_value = 1;
public struct YourStruct{

    public float yourValue{
        get {
            return _yourValue   default_value;
        }
        set {
            _yourValue= value - default_value;
        }
    }
    public float _yourValue;
}
  

Ответ №9:

Мое решение. Это также работает.

 public struct DisplayOptions
{
    public bool isUpon;
    public bool screenFade;

    public static DisplayOptions Build()
    {
        // Return default value
        return new DisplayOptions(true, true);
    }

    DisplayOptions(bool isUpon, bool screenFade)
    {
        this.isUpon = isUpon;
        this.screenFade = screenFade;
    }

    public DisplayOptions SetUpon(bool upon)
    {
        this.isUpon = upon;
        return this;
    }

    public DisplayOptions SetScreenFade(bool screenFade)
    {
        this.screenFade = screenFade;
        return this;
    }
}
  

Использовать значение по умолчанию

         // Use default
        UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build());
        // Use custome
        UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build().SetScreenFade(false));
        UIMaster.Instance.StartScreen("Screen 2", DisplayOptions.Build().SetUpon(false));
  

Ответ №10:

это должно сработать

 public struct MyStruct 
{
    private string myName;
    private int? myNumber;
    private bool? myBoolean;
    private MyRefType myType;

    public string MyName
    {
        get { return myName ?? "Default name"; }
        set { myName= value; }
    }
    public int MyNumber
    {
        get { return myNumber ?? 42; }
        set { myNumber = value; }
    }
    public bool MyBoolean
    {
        get { return myBoolean ?? true; }
        set { myBoolean = value; }
    }
    public MyRefType MyType 
    {
        get { return myType ?? new MyRefType(); }
        set { myType = value; }
    }

    //optional
    public MyStruct(string myName = "Default name", int myNumber = 42, bool myBoolean = true)
    {
        this.myType = new MyRefType();
        this.myName = myName;
        this.myNumber = myNumber;
        this.myBoolean = myBoolean;
    }
}
  

 [TestClass]
public class MyStructTest
{
    [TestMethod]
    public void TestMyStruct()
    {
        var myStruct = default(MyStruct);
        Assert.AreEqual("Default name", myStruct.MyName);
        Assert.AreEqual(42, myStruct.MyNumber);
        Assert.AreEqual(true, myStruct.MyBoolean);
        Assert.IsNotNull(myStruct.MyType);
    }
}
  

Комментарии:

1. Если вы явно передадите null for myName , вы получите значение по умолчанию, чего не должно произойти. Если вы назначаете null для MyType , та же проблема. У вас также не должно быть изменяемого ссылочного типа. Они злы. Наконец, наличие свойств на самом деле является важным семантическим отличием от наличия полей, как в OP, особенно когда дело доходит до работы с изменяемыми ссылочными типами (что является одной из причин, почему они такие злые).

2. @Servy что, если я удалю MyType и MyName и буду использовать типы значений только внутри структуры? это все еще неверно? Моя цель — создать структуру, MyConfig которая содержит несколько bool значений по умолчанию, имеющих значение true, чтобы я мог использовать ее в качестве параметра по умолчанию вот так: public void MyMethod(MyConfig config = default(MyConfig)) { //... } Спасибо.

3. Это все еще изменяет семантику структуры, изменяя поля на свойства, да. Если бы это было неизменяемо, это, вероятно, не было бы проблемой.

Ответ №11:

Это может сработать…

     public struct MyStruct
    {
        private bool _name;
        public string myName
        {
            get { return (_name ? myName : "Default name"); }
            set { _name = true; myName = value; }
        }
        private bool _num;
        public int myNumber 
        {
            get { return (_num ? myNumber : 42); }
            set { _num = true; myNumber = value; }
        }
        private bool _bool;
        public bool myBoolean
        {
            get { return (_bool ? myBoolean : true); }
            set { _bool = true; myBoolean = value; }
        }
        private bool _type;
        public MyRefType myType
        {
            get { return _type ? myType : new MyRefType(); }
            set { _type = true; myType = value; }
        }
    }
  

Не обращайте внимания на исключение StackOverflowException

Комментарии:

1. Я полагал, что bool, связанный с каждой переменной, будет инициализироваться значением false, а затем меняться на true, когда значение было изменено вне конструктора.

Ответ №12:

Существует обходной путь

 public struct MyStruct
{
    public MyStruct(int h = 1, int l = 1)
    {
        high = h;
        low = l;
    }
    public int high;
    public int low;
}
  

Комментарии:

1. Умная идея, но это не работает — вы пробовали это? Вызов new MyStruct() по-прежнему выдает объект с high и low , равным 0.

2. Ты прав @cdhowie, я только что протестировал компиляцию. По крайней мере, там указано, что это не работает, поэтому никто другой не попробует это, хахаха.