How to Initialize Var

How to initialize var?

C# is a strictly/strongly typed language. var was introduced for compile-time type-binding for anonymous types yet you can use var for primitive and custom types that are already known at design time. At runtime there's nothing like var, it is replaced by an actual type that is either a reference type or value type.

When you say,

var x = null; 

the compiler cannot resolve this because there's no type bound to null. You can make it like this.

string y = null;
var x = y;

This will work because now x can know its type at compile time that is string in this case.

Initialize var to null

Simply use FirstOrDefault() instead. The whole point of FirstOrDefault is to return the first element of the sequence if it exists, or the default value of the element type (i.e. null for all reference types) otherwise.

Note that in other cases where you wish to check for the existence of any elements, using Any() can sometimes be more efficient than Count() > 0 - it depends on the exact context, but IMO it's a simpler way of expressing what you're looking for anyway.

Why var in JAVA 10 can not be initialized to null?

There are (at least) three possible type inference strategies the compiler could apply to var o = null:

  • pick Void
  • pick Object
  • look for a later initialization and pick that type

All of them are technically feasible, so the question emerges, which one makes the most sense for developers.

Clearly, Void is pretty useless and I would argue that Object is not much more useful, either. While correct, picking either of these types is unlikely to help the developer write better and more readable code.

The last option, looking for an initialization, was not adopted on purpose to avoid so-called action-at-a-distance errors:

var parent = null;
// imagine some recursion or loop structure, so this makes more sense
processNode(parent); // expects a parameter of type `Node`
parent = determineParent(); // returns a parameter of type `Node`

If the compiler inferred Node for parent because determineParent() returns it, this would compile. But the code is fragile because changes to the last line, might lead to a different type chosen in the first line and hence to compile errors on the second line. That's not good!

We're used to the fact that changing a type's declaration can lead to errors down the road but here the change (line 3), its effect (line 1), and consequent error (line 2) can be pretty far apart, this making it much more complicated for developers to understand or, better, predict what happens.

By keeping the type inference rules simple, developers have it easier to form a simple but correct mental model of what's going on.

Addendum

There are doubts whether option 3, inferring the type from a later initialization, is indeed technically feasible. My opinion (that it is) is based on my understanding of JEP 286, specifically:

On the other hand, we could have expanded this feature to include the local equivalent of "blank" finals (i.e., not requiring an initializer, instead relying on definite assignment analysis.) We chose the restriction to "variables with initializers only" because it covers a significant fraction of the candidates while maintaining the simplicity of the feature and reducing "action at a distance" errors.

Similarly, we also could have taken all assignments into account when inferring the type, rather than just the initializer; while this would have further increased the percentage of locals that could exploit this feature, it would also increase the risk of "action at a distance" errors.

Initialize value of 'var' in C# to null

var variables still have a type - and the compiler error message says this type must be established during the declaration.

The specific request (assigning an initial null value) can be done, but I don't recommend it. It doesn't provide an advantage here (as the type must still be specified) and it could be viewed as making the code less readable:

var x = (String)null;

Which is still "type inferred" and equivalent to:

String x = null;

The compiler will not accept var x = null because it doesn't associate the null with any type - not even Object. Using the above approach, var x = (Object)null would "work" although it is of questionable usefulness.

Generally, when I can't use var's type inference correctly then

  1. I am at a place where it's best to declare the variable explicitly; or
  2. I should rewrite the code such that a valid value (with an established type) is assigned during the declaration.

The second approach can be done by moving code into methods or functions.

Declare a var without initializing it... just yet

The correct answer here is to drop the use of var and to correctly specify the type of existingUsers outside the try...catch block:

List<User> existingUsers = null; // or whatever is the right type!
try
{
existingUsers = Repo.GetAll(); // This may crash, and needs to be in a try
}
catch (Exception)
{
throw new Exception("some error, don't bother");
}
if (existingUsers.Count > 0)
{
//some code
}

Initializing var type in LINQ

var is not a type - it means "I don't care to (or can't) specify what the type is - let the compiler do it for me".

In your case, you're assigning it the result of one of two queries, one of which returns an anonymous type, so you can't specify the type since you don't know the name of the anonymous type (hence the term "anonymous").

In order to use var, the compiler needs some expression at initialization to know what the actual type is.

I'd suggest something like:

var lstCurrent = Type==1 
? _context.Customers().Where(t =>t.type=="current").Select(c => new { c.LastName, c.City})
: _context.Customers().Where(...).Select(...)

But note that your "selects" must return the same type (or anonymous types with the exact same fields) or you won't be able to use var.

In the end I would try to bake the condition into your Where clause for less repetetive code:

bool isTypeOne = Type==1;

var lstCurrent = _context.Customers()
.Where(t => isTypeOne ? t.type=="current" : ...)
.Select(c => new { c.LastName, c.City})

How to initialize var Record parameter

Using just FillChar is wrong here. If any of the WideString members are not empty, then you will leak them this way. Instead I suggest the following:

Finalize(IPInfo);
FillChar(IPInfo, SizeOf(TIPInfo), 0);

Or another way is to define a default record as a typed constant:

const
DefaultIPInfo: TIPInfo = ();

Then you can use simple assignment:

IPInfo := DefaultIPInfo;

In modern versions of Delphi you can instead use this much more readable code:

IPInfo := Default(TIPInfo);

For more on this particular subject, refer to these topics:

  • Delphi "default" keyword with Record types in older Delphi versions.
  • How to properly free records that contain various types in Delphi at once?

Note that the leak in your code is hard to find because WideString variables are implemented as COM BSTR objects, and allocated on the COM heap. Therefore, if you use memory leak detection for the Delphi memory manager the leak will not be detected because it is leaked from a different heap.

In your case, because your record is a managed type, and contains only managed types, then you could use an out parameter to good effect. For managed types, out parameters mean that the compiler will generate code, at the call site, to default initialize the record before passing it in.

Consider the following program:

{$APPTYPE CONSOLE}

type
TRec = record
Value: WideString;
end;

procedure Foo1(var rec: TRec);
begin
end;

procedure Foo2(out rec: TRec);
begin
end;

procedure Main;
var
rec: TRec;
begin
rec.Value := 'Foo';
Foo1(rec);
Writeln(rec.Value);
Foo2(rec);
Writeln(rec.Value);
end;

begin
Main;
end.

The output is:


Foo

If your record contains a mix of managed and unmanaged types, then the situation is not so good.

{$APPTYPE CONSOLE}

type
TRec = record
Value1: WideString;
Value2: Integer;
end;

procedure Foo1(var rec: TRec);
begin
end;

procedure Foo2(out rec: TRec);
begin
end;

procedure Main;
var
rec: TRec;
begin
rec.Value1 := 'Foo';
rec.Value2 := 42;
Foo1(rec);
Writeln(rec.Value1);
Writeln(rec.Value2);
Foo2(rec);
Writeln(rec.Value1);
Writeln(rec.Value2);
end;

begin
Main;
end.

The output is:


Foo
42

42

Only the managed members are default initialized for out parameters. So your best bet is to default initializing the variable yourself, even if it is passed as an out parameter.

More on out parameters can be found here: What's the difference between "var" and "out" parameters?



Related Topics



Leave a reply



Submit