2017-01-27 8 views
0

Я пытаюсь понять, существует ли какая-либо разница между присвоением null экземпляру класса и просто объявлением этого класса. Для примера, у меня есть класс:В чем разница между присваиванием null экземпляру класса и просто объявлением

public class MyClass 
{ 
    public string FirstProperty { get; set; } 
    public int SecondProperty { get; set; } 
} 

Затем я создаю два экземпляра класса:

MyClass Instance1 = null; 
MyClass Instance2; // just declaration 

есть ли разница между Instance1 и Instance2?
Да, это безопасно, и это хорошая привычка иметь Instance2 с декларацией?

+1

Первый является переменной, которая была инициализирована с 'null', последний является varibale, не инициализирована явно, поэтому получает значение по умолчанию, которое тоже нуль. Разница заключается в следующем: читаемость, все знают, что нуль является желательным и ожидаемым. Если у вас есть 'out' paramater, компилятор заставляет вас что-то присваивать, даже если это« null ». –

+0

Например, Resharper предложит удалить в этом случае назначение «null». – Fabio

+1

Да, есть разница. Во втором случае, если значением является поле, ему автоматически присваивается значение по умолчанию, но если это локальная переменная, это не будет. Если он затем «считывается» кодом, это приводит к ошибке компилятора «использование неназначенной локальной переменной». – stuartd

ответ

5

Затем я создаю два экземпляра класса:

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

Есть ли разница между Instance1 и Instance2?

Это зависит от того, где вы это сделали.

Если код находится в пределах class или struct то вы создали два поля, оба из которых будут установлены в null первоначально, если конструктор не делает что-то другое.

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

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

Вторая не была назначена, и поэтому незаконно делать что-либо другое, кроме как назначить ей что-нибудь (нуль или экземпляр). Любая попытка сделать что-либо, прежде чем он определенно будет установлен, будет ошибкой компилятора. Например .:

MyClass Instance2; 
if (valueThatJustHappensToAlwaysBeTrue) 
{ 
    Instance2 = new MyClass(); 
} 
bool isNull = Instance2 == null; // Error! Not guaranteed to be assigned strongly enough for the compiler to know. 

Это да, это безопасно, и это хорошая привычка иметь Instance2 только с декларацией?

Когда это возможно, это хорошая привычка быть объявляя как можно ближе к первому заданию (инициализации), как это возможно, в идеале, в то же время:

MyClass instance = new MyClass(); 

Однако, если у Вас есть несколько различные возможные пути, такие как:

MyClass instance; 
if (boolValue) 
{ 
    instance = new MyClass(1); // Yes, I know there's no int-taking ctor on your class, but it defeats the argument when the bare constructor is the only one available, so let's say there is. 
} 
else if (otherBoolValue) 
{ 
    throw new SomeException(); 
} 
else if (someIntValue > 42) 
{ 
    instance = new MyClass(3); 
} 
else 
{ 
    instance = new MyClass(9); 
} 

Теперь это невозможно, чтобы добраться до конца этой цепи с неинициализированной instance. Либо он будет установлен, либо исключение будет выброшено. Если бы мы подумали, что это может быть «безопаснее» начинаться с MyClass instance = null, мы могли бы скрыть ошибку. Вышеупомянутая логика предназначена для присвоения ей чего-то для каждого пути и из-за правила, что вы не можете использовать экземпляр, который не гарантированно назначается, ошибка в этом вызовет ошибку компилятора, и ошибка будет очевидна , Если он присваивается нулевому значению «заполнителя», чтобы начать с такой ошибки, это не будет столь очевидным, и может вызвать ошибку.

Так что в таких случаях, как голая декларация, лучше.

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

+0

Thaks для вашего ответа, теперь я понимаю, когда и почему использовать экземпляр MyClasss; –

5

В ваших примерах вы не создаете экземпляр . Вы объявляете две переменные .

К первому вы явно указываете null. Ко второму вы ничего не назначаете. Поэтому он содержит значение по умолчанию, которое составляет null для ссылочных типов.

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

MyClass Instance1 = null; 
MyClass Instance2; 
Console.WriteLine(Instance1); // fine 
Console.WriteLine(Instance2); // raises error CS0165 

Как stuartd и Кайл заметил, локальные переменные не могут инициализируется компилятором, что объясняет ошибку. Вы не можете полагаться на переменную, имеющую какое-либо конкретное значение, прежде чем назначать ее, будь то null или созданный экземпляр MyClass. Поля и свойства инициализируются значением по умолчанию для типа (null для ссылочных типов).

+0

В случае локальных переменных имеет значение, что неинициализированная переменная фактически будет содержать null? Компилятор не позволит вам получить доступ к неинициализированной локальной сети, так что действительно ли важно, что он содержит? – Kyle

+0

@Kyle вправо, после просмотра комментария stuartd Я не уверен, что локальные переменные инициализированы вообще ... возможно, нет, почему еще компилятор выбросит эту ошибку ... просто пытаясь получить право формулировки для редактирования моего ответа , –

+0

@ RenéVogt взгляните на ответ Джона Ханны. У него есть хорошее объяснение. Кстати, спасибо, за ваш ответ. –

-2

Они одинаковы.

Фактически, если вы используете ReSharper, он скажет вам, что значение «= null» является избыточным.

как для вашего второго вопроса. Я не объявляю много переменных, не присваивая их сразу.

так что если вы просто объявить его как

MyClass Instance2; 

затем установить его.

MyClass = new Instance2; 

Instance2.FirstProperty = "some string"; 
Instance2.SecondProperty = 1; 

Это может все получить объединены в одном заявлении

var MyClass = new Instance2 { FirstProperty = "some string", SecondProperty = 1 }; 
+2

Это не отвечает на вопрос. – InBetween

+0

@InBetween как вы себя представляете? Первый вопрос. они одинаковые. Второй вопрос - это просто объявить переменные. нет, в большинстве случаев вы хотите их инициализировать. – Fran

+0

ОП спрашивает, в чем разница между 'MyClass instance1' и' MyClass instance2 = null'. Первая - это неинициализированная переменная, а более поздняя - переменная, инициализированная символом 'null', что представляет собой две очень разные вещи в отношении компилятора. – InBetween

1

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

Пример:

class Program 
    { 
     class MyClass 
     { 
      public void MyMethod() { } 
     } 

     static void Main(string[] args) 
     { 
      // Set a breakpoint here to see that mc = null. 
      // However, the compiler considers it "unassigned." 
      // and generates a compiler error if you try to 
      // use the variable. 
      // try Console.WriteLine(mc); 
      // it will return error CS0165: Use of unassigned local variable `mc' 
      MyClass mc; 

      // Now the variable can be used, but... 
      mc = null; 

      // ... a method call on a null object raises 
      // a run-time NullReferenceException. 
      // Uncomment the following line to see for yourself. 
      // mc.MyMethod(); 

      // Now mc has a value. 
      mc = new MyClass(); 

      // You can call its method. 
      mc.MyMethod(); 

      // Set mc to null again. The object it referenced 
      // is no longer accessible and can now be garbage-collected. 
      mc = null; 
     }