In my Index Action in my Controller I have this:
public ActionResult Index(string sortOrder)
{
var model;
ViewBag.CitySortParm = String.IsNullOrEmpty(sortOrder) ? "City_desc" : "";
switch (sortOrder)
{
case "City_desc":
model = this.UnitOfWork.AddressRepository.Get().OrderBy(a => a.City);
break;
}
return View(model);
}
This doesn't work, I always get the error: Implicitly-typed local variables must be initialized
Why doesn't this work and how can I fix this?
Why doesn't this work
Two reasons:
You're trying to use var without specifying an initial value. You can't do that - the compiler can't infer the type.
You're trying to read from the model variable when it may not be initialized - if sortOrder isn't City_desc what would you expect to be returned? And what would that type be?
and how can I fix this?
That depends on what you want to happen if sortOrder isn't City_desc. Work out what value you'd want to return, and make sure it's returned.
Basically, you can't read from a local variable until it's definitely assigned - in other words, until the compiler can prove (by the rules of C#) that by the time you get there, it will have been assigned a value.
In terms of the implicit typing - are you always going to use the same type for your view? If so, just explicitly declare the type of model, as well as fixing the definite assignment part. If not, I'd consider returning within the switch/case statement - I don't think your model local variable is actually helping you much.
The first problem is that the type of the model variable cannot be inferred if you just declare it with var model;.
You should explicitly declare its type.
Also, as Jon Skeet correctly pointed out, you must initialize your model somehow before the return... call. Try to imagine what you model's value is if sortOrder is not "City_desc"...
Implicitly-typed local variables must be initialized
Yes will have to initialize it inline to make it work.
var model = default(MyType);//This should work
Or just declare the type explicitly.
MyType model = default(MyType);//Or null if it is a reference type
You have to initialize an implicitly declared variable before using it. A simple example:
var x;
if(true)
x = "true";
This will get you the same error. This should be hapening:
var x = "";
if(true)
x = "true";
Same goes for your code. Initialize it before using it. Say your model is of type AddressViewModel, then this should work:
var model = new AddressViewModel();
ViewBag.CitySortParm = String.IsNullOrEmpty(sortOrder) ? "City_desc" : "";
switch (sortOrder)
{
case "City_desc":
model = this.UnitOfWork.AddressRepository.Get().OrderBy(a => a.City);
break;
}
return View(model);
Related
I have a method:
public Task<IEnumerable<MyDetails>> HandleAsync(...)
Within this method, I simply build a list of items.
IEnumerable<MyDetails> myDetails = new List<MyDetails> {
new MyDetails{ Name = 'John' },
new MyDetails{ Name = 'James' },
new MyDetails{ Name = 'Anne' },
};
And then return the result.
return Task.FromResult(insuraceTypeDetails);
In a code review, I'm being asked to use var instead of IEnumerable<MyDetails>
However, when attempting that, I get an error on my return:
Cannot convert type Threading.Tasks.Task<List> to
Threading.Tasks.Task<IEnumerable>
I thought this would be fine, but the compiler will not allow it. Can I use var here? Or am I correctly explicitly telling the compiler what we need?
In a code review, I'm being asked to use var instead of IEnumerable<MyDetails>
var will infer the type the variable from the intialization, in this cast a List<MyDetails>. But as you have discovered, you need the variable to be an IEnumerable<MyDetails> to match the return type.
You could do something goofy like convert it to an IEnumerable:
return Task.FromResult(insuraceTypeDetails.AsEnumerable());
or do an explicit cast:
return Task.FromResult((IEnumbrable<MyDetails>)insuraceTypeDetails);
but those are just working around a dogmatic coding style. There's absolutely nothing wrong with explicitly declaring the variable type, especially when using the inferred type does not work.
Use IEnumerable<MyDetails> and explain to your colleagues why var is not the right choice here.
Can I initialize var with null or some empty value?
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.
Well, as others have stated, ambiguity in type is the issue. So the answer is no, C# doesn't let that happen because it's a strongly typed language, and it deals only with compile time known types. The compiler could have been designed to infer it as of type object, but the designers chose to avoid the extra complexity (in C# null has no type).
One alternative is
var foo = new { }; //anonymous type
Again note that you're initializing to a compile time known type, and at the end its not null, but anonymous object. It's only a few lines shorter than new object(). You can only reassign the anonymous type to foo in this one case, which may or may not be desirable.
Initializing to null with type not being known is out of question.
Unless you're using dynamic.
dynamic foo = null;
//or
var foo = (dynamic)null; //overkill
Of course it is pretty useless, unless you want to reassign values to foo variable. You lose intellisense support as well in Visual Studio.
Lastly, as others have answered, you can have a specific type declared by casting;
var foo = (T)null;
So your options are:
//initializes to non-null; I like it; cant be reassigned a value of any type
var foo = new { };
//initializes to non-null; can be reassigned a value of any type
var foo = new object();
//initializes to null; dangerous and finds least use; can be reassigned a value of any type
dynamic foo = null;
var foo = (dynamic)null;
//initializes to null; more conventional; can be reassigned a value of any type
object foo = null;
//initializes to null; cannot be reassigned a value of any type
var foo = (T)null;
This is the way how to intialize value to var variable
var _myVal = (dynamic)null;
Why wouldn't this be possible?
var theNameOfTheVar = (TheType)null;
eg:
var name = (string)null;
Simple as that.
A var cannot be set to null since it needs to be statically typed.
var foo = null;
// compiler goes: "Huh, what's that type of foo?"
However, you can use this construct to work around the issue:
var foo = (string)null;
// compiler goes: "Ah, it's a string. Nice."
I don't know for sure, but from what I heard you can also use dynamic instead of var. This does not require static typing.
dynamic foo = null;
foo = "hi";
Also, since it was not clear to me from the question if you meant the varkeyword or variables in general: Only references (to classes) and nullable types can be set to null. For instance, you can do this:
string s = null; // reference
SomeClass c = null; // reference
int? i = null; // nullable
But you cannot do this:
int i = null; // integers cannot contain null
Nope. var needs to be initialized to a type, it can't be null.
Well, I think you can assign it to a new object. Something like:
var v = new object();
var just tells the compiler to infer the type you wanted at compile time...it cannot infer from null (though there are cases it could).
So, no you are not allowed to do this.
When you say "some empty value"...if you mean:
var s = string.Empty;
//
var s = "";
Then yes, you may do that, but not null.
you cannot assign null to a var type.
If you assign null the compiler cannot find the variable you wanted in var place.
throws error: Cannot assign <null> to an implicitly-typed local variable
you can try this:
var dummy =(string)null;
Here compiler can find the type you want so no problem
You can assign some empty values.
var dummy = string.Empty;
or
var dummy = 0;
you can't initialise var with null, var needs to be initialised as a type otherwise it cannot be inferred, if you think you need to do this maybe you can post the code it is probable that there is another way to do what you are attempting.
Thank you Mr.Snake, Found this helpfull for another trick i was looking for :) (Not enough rep to comment)
Shorthand assignment of nullable types.
Like this:
var someDate = !Convert.IsDBNull(dataRow["SomeDate"])
? Convert.ToDateTime(dataRow["SomeDate"])
: (DateTime?) null;
How about default keyword?
var foo = default(SomeClass);
Compiler doesn't lose what type the foo has. foo will have default value of reference type, null
I have this code
// was before var groupTransactions;
IEnumerable<IGrouping<TransactionsGroupedByMonthYear, TransactionDto>> groupTransactions = null;
DollarCapPeriod dollarCapPeriod = (DollarCapPeriod)Enum.Parse(typeof(DollarCapPeriod), reward.DollarCapPeriod);
switch (dollarCapPeriod)
{
case DollarCapPeriod.Monthly:
groupTransactions = filterdTransactions.GroupBy(x => new { x.TransactionDate.Month, x.TransactionDate.Year });
break;
case DollarCapPeriod.Yearly:
groupTransactions = filterdTransactions.GroupBy(x => new TransactionsGroupedByMonthYear { Month = 0, Year = x.TransactionDate.Year });
break;
default:
break;
}
I want to do something like that but it wants the type initialized. So I am wondering is there away around this or do I have to make some sort of concrete type? If so how do I do it again with groupBy?
Sample Data
1/14/2012,5,Gas
1/15/2012,5,Gas
1/16/2012,5,Gas
1/17/2012,5,Gas
Edit
Currently I am trying to do now use a concrete backing but I need to filter by 2 different ways. Sometimes I need only the "Year" and some times I need "Month" and "Year"
I don't know how to do this. They both need to go in the same variable so they need to be the same type.
First of all, don't confuse anonymous types with inferred types. The var keyword does not necessarily mean a type is anonymous. An anonymous type does not have a name as far as the developer is concerned (the compiler, of course, gives it one, but that's an implementation detail that you can and should ignore). An inferred type, using the var keyword, could have a name, you just didn't have to type it.
It is never allowed to say:
var x;
because there is no information from which to infer the type.
In the general case, you can use anonymous types in scoping situations like loops, try/catch, etc. by initializing a new instance of the anonymous type ahead of time:
var x = new { Month = 0, Year = 0 };
switch (foo)
{
case 1:
x = new { transaction.Month, transaction.Year };
}
As long as the field names and types match between the two anonymous type initializers, the "actual" type is the same.
In your specific case, though, you don't want just an anonymous type, because GroupBydoesn't returning an anonymous type. It returns an IEnumerable<IGrouping<TKey, TSource>>, and in your case only TKey is anonymous. While it is possible to declare that type in code, it's very hard, and would involve temporary variables being declare solely to get their anonymous typeof() information out of them. It would almost certainly be an unmaintainable mess.
Instead you should just declare a type to use at your group key. It's still going to be ugly, on account of the nested generics, but nowhere near as bad as trying to use the anonymous type.
public class TransactionGroup
{
public int Month;
public int Year;
}
and get your grouping:
IEnumerable<IGrouping<TransactionGroup, Transaction>> groupTransaction;
var period = (Period)Enum.Parse(typeof(Period), record.Period);
switch (period)
{
case Period.Monthly:
groupTransaction = transactions.GroupBy(x => new TransactionGroup
{
Month = x.TransactionDate.Month,
Year = x.TransactionDate.Year
});
break;
case Period.Yearly:
groupTransaction = transactions.GroupBy(x => new TransactionGroup
{
Month = 0,
Year = x.TransactionDate.Year
});
break;
default:
break;
}
What about using
dynamic groupTransaction
No, in this case you have to define a concrete type, if you want to declare the groupTransaction variable before the switch. You could use anonymous types with the var keyword only when you are declaring and immediately initializing the variable.
So you could define a model which will be used for the grouping:
public class MyModel
{
public int Month { get; set; }
public int Year { get; set; }
}
and then:
IEnumerable<IGrouping<MyModel, SourceType>> groupTransaction = null;
DollarCapPeriod dollarCapPeriod = (DollarCapPeriod)Enum.Parse(typeof(DollarCapPeriod), reward.DollarCapPeriod);
switch (dollarCapPeriod)
{
case DollarCapPeriod.Monthly:
groupTransaction = filterdTransactions.GroupBy(x => new MyModel { x.TransactionDate.Month, x.TransactionDate.Year });
break;
case DollarCapPeriod.Yearly:
break;
default:
break;
}
You can't do that with the "var" keyword. The "var" keyword is a convenience keyword that allows you to implicitly declare variables based on the type being assigned. You can achieve this using several methods including generic and dynamic typing.
note: using 'object' this way is considered bad coding practice
You could just set the variable as an object:
object groupTransaction = null;
You could also declare it dynamic in which case you lose intellisense support and some compile-time syntax checking, plus you may have to cast the variable in certain cases:
dynamic groupTransaction = null;
[update]
I'm sorry, i should tag this question
as MVC-2, I pass result of query into
view's model, so i must specify type
of my model in View's header
defintion. I declare it like this:
Inherits="System.Web.Mvc.ViewPage<IQueryable<dynamic>>"
how ever nothing changed and none of
answers doesn't work for me :(.
finally i used an ModelView class as
helper to put my query result in it.
:(
[/update]
I have a query like this:
IQueryable<dynamic> result = from d in KiaNetRepository.KiaNetEntities.Discounts
where d.AgentTypeID == agentTypeId
select new { d.Category, d.DiscountValue, d.PriceConfige };
then i retrive value in my view like this:
foreach(var item in result){
Category cat = item.Category; // throws exception 'object' does not contain a definition for 'Category'
//...
}
note that type of query as IQueryable is anonymouse class...
Try to declare names explicitly:
select new { Category = d.Category, DiscountValue = d.DiscountValue, PriceConfige = d.PriceConfige }
If you are not forcing result to be of IQueryeable<dynamic> for any specific reason, I would recommend using var result = .... This will let the compiler make result of type IQueryable<T> with T the type of the anonymous class that you create using new { ... } in the select. There is no necessity for using dynamic from the code that you show here.
If you replace the inappropriate declaration IQueryable<dynamic> by var, sure it works, I've just also tested it.
Your problem is that your foreach loop being in the view page gets compiled into a separate assembly. Since anonymous types are internal the dynamic doesn't see it because of the permissions don't allow it.
Simplest fix is to call ToList() on your query statement and then select each anonymous type and copy parameters to a declared class or expandoobject.
I've been introducing myself to LinqToSQL lately through a poorly-made project at work. I'm curious as to why this works:
var territories = db.Territories.Where(t => t.PendingUserCount > 0);
But this results in a compilation error:
var territories = db.Territories;
if (someCondition)
territories = territories.Where(t => t.PendingUserCount > 0);
// Cannot implicitly convert 'System.Linq.IQueryable<Territory> to System.Data.Linq.Table<Territory>
I've also tried to call db.Territories.ToList(), but to no avail.
I'm sure it's just a misunderstanding of how Linq works, but I'd be appreciative if someone could help me out.
db.Territories returns a table object. Hence the 'var' will be of type System.Data.Linq.Table. Later you try (based on some condition) to assign something of type System.Linq.IQueryable to the variable. As .NET is strongly typed, the compiler throws an error.
Variables of type var will be assigned a type when they get assigned first. That's how I try to remember myself.
For this type of cumulative Where, you need to tell the compiler to use IQueryable<T>:
IQueryable<Territory> territories = db.Territories;
if (someCondition)
territories = territories.Where(t => t.PendingUserCount > 0);
... etc
Alternative:
var territories = db.Territories.AsQueryable();
if (someCondition)
territories = territories.Where(t => t.PendingUserCount > 0);
change to this
var territories = db.Territories;
to
IQueryable territories = db.Territories.Where(t => t.PendingUserCount > 0);
The reasoning is that by calling db.Territories, you are getting all the data back, returning it in a linq.table object. Db.Territores.where(... will return an IQueryable object instead.
One of the potentially confusing things about "var" is that its type is determined at compile time, so you can't assign a range of different types to it. People with experience of dynamic languages, like Python, sometimes get confused by this, at first.
Your var territories is typed as a System.Data.Linq.Table<Territory> initially and then you are trying to assign the results of a Where clause (which is of type System.Linq.IQueryable<Territory>) to it.
Remember that the compiler infers the type of a var at assignment so once it is assigned the type cannot be changed.
Try something like this:
System.Linq.IQueryable<Territory> territories;
if (someCondition)
territories = db.Territories.Where(t => t.PendingUserCount > 0);
Because you've typed "var" as a Table<Territory> and then try to reassign it as a IQueryable<Territory>.
This is equivalent to saying
var i = 0
i = "a string";
EDIT: To clarify, var is implicitly strong typed at compile time not run time, unlike dynamically typed scripting language.
You can't re-assign a var to a different type after you've declared it. So your var declaration has already typed it to System.Data.Linq.Table.
It's a fire once thing.
You need to use the IQueryable tyupe as others have suggested:
also this linq query maybe also work:
var query = from territories in db.Territories
where territories.SomeProperty == SomeCondition
where territories.PendingUserCount > 0
select territories;