This question already has answers here:
var keyword not always working?
(5 answers)
Closed 8 years ago.
Consider this working code:
RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
rcb.Items.Add(new RadComboBoxItem("One", "1"));
rcb.Items.Add(new RadComboBoxItem("Two", "2"));
rcb.Items.Add(new RadComboBoxItem("Three", "3"));
// check all items
foreach (RadComboBoxItem i in rcb.Items)
{
i.Checked = true;
}
If I replace the foreach loop with var, it does not compile:
RadComboBox rcb = new RadComboBox(); // Telerik.Web.UI.RadComboBox
rcb.Items.Add(new RadComboBoxItem("One", "1"));
rcb.Items.Add(new RadComboBoxItem("Two", "2"));
rcb.Items.Add(new RadComboBoxItem("Three", "3"));
// check all items
foreach (var i in rcb.Items)
{
i.Checked = true;
}
The error is:
'object' does not contain a definition for 'Checked' and no extension method 'Checked' accepting a first argument of type 'object' could be
found (are you missing a using directive or an assembly reference?)
So, I wonder, what are the conditions when you cannot use var?
Edit: Just to clarify, I wasn't simply asking "why doesn't this work"? Here's an example that could make the problem more obvious:
List<Object> stuff = new List<object>();
stuff.Add("one");
stuff.Add("two");
stuff.Add("three");
foreach (string s in stuff)
{
if (s.Length > 3)
// do something
}
Now if you change string s in stuff to var s in stuff, it obviously isn't going to compile. You can't just willy-nilly go and replace any type with var and expect it to compile. The error in my thinking that led me to propose the question in the first place was that I assumed that since rcb.Items is of type RadComboBoxItemCollection, that the enumerable type would be RadComboBoxItem, and this is not the case.
Unfortunately my example tainted the question by leading some of the answers down the "why does substituting a type with var not work?" path. Paulo actually answered with what I was looking for (others partially did too), which is why I have awarded it the best answer.
You can use var any time you want to use implicit typing. That is, when the type can be derived from the declaration. It would seem in this case that the actual type of Items is a collection of objects. So that's all var knows in that declaration.
By explicitly declaring the type as RadComboBoxItem you are essentially doing something very similar to this (but with more compile-time checking):
foreach (var i in rcb.Items)
{
(i as RadComboBoxItem).Checked = true;
}
The object instances in Items can be implicitly converted to RadComboBoxItem, but var doesn't know to do that. You have to explicitly do that. (Personally I would consider that an odd design of RadComboBox, but it could be a holdover from earlier design decisions. Collections in .NET weren't always as strongly-typed as they are today. Things like ArrayList used to cause similar problems.)
The reason this is occurring in that for loop is because the compiler is unable to infer the type of the enumerable object. This occurs in older code that predates generics - with generics, the compiler is able to infer that the next item from the iterator will be of type T. However, without generics, we return a non-generic IEnumerator type, whose iterations only expose an object.
Remember that a for loop is basically syntactic sugar for invoking the Next() method on an enumerator, and then checking the Current property on aforementioned enumerator. Older types that pre-date generics did not have generics in their Enumerator, so they would always return an object (here is the actual method invoked).
The newer, strongly-typed enumerators however return a type of T when you call Current, so the type can be inferred.
Another example of this is the System.Data namespace when trying to iterate over a DataTable's rows.
In this case you should explicitly state the type of the variable.
I appreciate this might be long winded but I was trying to provide the reason why the type can't be inferred here and a little background because I was confused when I first had the issue trying to enumerate a DataRowCollection.
TLDR:
you're iterating over a non-generically typed enumerator when you do for. Because it is not generically typed, the enumerator returns instances of Object. Ergo, you get object instances back and can only use the methods available to Object until you cast the variable.
The RadComboBox Items property will return a RadComboBoxItemCollection, which implements both IEnumerable<RadComboBoxItem> and the non-generic IEnumerable.
Although I'm not familiar with Telerik's products, I suspect previous versions didn't implement IEnumerable<RadComboBoxItem>.
According to the C# language specification, it should work (C# >= 3.0 section 8.8.4), in a simpler English:
If the expression's type is an array, it's cast to IEnumerable and the element type is the array's element type
(C# >= 4.0) If the expression's type is dynamic, it's cast to IEnumerable and the element type is dynamic if var is used, object otherwise
If the expression's type quacks like IEnumerable and the quacked enumerator quacks like IEnumerator, the element type is the type of the Current property of the quacked enumerator
Quacking like IEnumerable means it has:
A public non-static unambiguous GetEnumerator method with a class, struct or interface return type
Quacking like IEnumerator means it has:
A public non-static Current property
A public non-static unambiguous MoveNext method with a bool return type
If any condition is not met once a GetEnumerator method is found, an error is produced
If the expression's type implements a single IEnumerable<T>, the element type is T
If the expression implements more than one generic IEnumerable<T>, an error is produced
If the expression implements the non-generic IEnumerable, the element type is object
Otherwise, an error is produced
Your case should fall in the emphasized bullet given the current version.
However, if you're using a previous version and my guess is right, your case is the second to last.
As rcb.Items returns a RadComboBoxItemCollection, i will become of type object.
Either you need to convert it to a RadComboBoxItem
(i as RadComboBoxItem).Checked = true;
Or modify the loop like
foreach (var i in rcb.Items.Cast<RadComboBoxItem>())
{
i.Checked = true;
}
Hope it helps.
Using var is like using type Object in that case since Items is a collection of Object.
If you wish to use the expression i.Checked = true, you must transtype your i into a RadComboBoxItem first.
foreach(RadComboBoxItem i in rcb.Items)
{
i.Checked = true;
}
This would be the best if you wish to use this argument (except if rct.Items contains something else than RadComboBoxItem types, in that case you may have to find the right interface.
Related
I'm trying to check if a list of Quests contains a Quest of Type StartingQuest. If it does, I want to increase the field OgresKilled that belongs to the Quest by 1.
The following code does not work.
I get the error message in the second line of code where I check ... == StartingQuest:
Error CS0119 StartingQuest is a type, which is not valid in the
given context
This does not make sense to me, as I explicitly want to check against the type.
if (Globals.ActiveQuests.Contains(new StartingQuest()))
{
StartingQuest a = Globals.ActiveQuests.Find(a => a.GetType == StartingQuest);
a.OgresKilled += 1;
}
Can someone please explain what my mistake is? And is this an efficient way of doing what I want to do?
Other answers have explained aspects that are incorrect in the code in the question:
It checks for equality between an existing quest and a brand-new StartingQuest instance, which is unlikely to ever be true
It tries to use the method group GetType directly, instead of calling the method (as a.GetType())
It tries use a type name directly in the == expression, where you intended to evaluate a Type reference - typeof will help there.
I would suggest a slight modification of SomeBody's answer (in fact explaining an option already present in a comment).
Currently both the question and all the answers here are checking for the exact type StartingQuest, which is somewhat brittle. It's usually more useful to check whether something is a target type or a derived type, even if you don't currently have any derived types.
LINQ provides the OfType method which will find elements of a sequence in precisely that manner - and the resulting sequence has an element type of that "target type", which means that if you need to access something that's in StartingQuest but not the base type (Quest or whatever it is), you'll be fine.
So I'd definitely use:
// Import the System.Linq namespace to make the OfType and FirstOrDefault
// extension methods available
using System.Linq;
...
var startingQuest = Globals.ActiveQuests.OfType<StartingQuest>().FirstOrDefault();
if (startingQuest is object)
{
startingQuest.OgresKilled++;
}
The use of is object instead of != null is just to use the more modern idiom for checking whether something is non-null.
Your if check will always fail, because you look for a new instance of StartingQuest. If it is a reference type, this will never be true, because the newly created instance will have a different reference than all the quests in the list. Moreover, you are iterating the list twice, one time in the Contains method, and one time in the Find method. This will fix these issues:
var startingQuest = Globals.ActiveQuests.FirstOrDefault(x => x.GetType() == typeof(StartingQuest));
// as an alternative, this is also possible:
// var startingQuest = Globals.ActiveQuests.OfType<StartingQuest>().FirstOrDefault();
if(!(startingQuest is null))
{
startingQuest.OgresKilled += 1;
}
FirstOrDefault returns the first instance that matches a criterion. If no item is found, the default value of that type is returned (null for reference types). Hence you check whether the return value is not null. If it's not null, an item was found and you can increment your OrgresKilled variable.
Your error is because you have to get the type of StartingQuest:
a.GetType() == typeof(StartingQuest)
GetType returns a Type, so you also need an instance of the type Type to compare to. That's what typeof does, it returns an instance of Type that corresponds to the type of its argument.
This question already has answers here:
What are generics in C#? [closed]
(3 answers)
Closed 5 years ago.
I know many programming and scripting languages, but I never saw something like this before:
camera = GetComponentInParent<Camera>();
Is this normal in C#? Why don't we have to pass the parameter Camera like this:
camera = GetComponentInParent(Camera);
Where can I inform myself about why it is like this?
The parameter in the first version is a type parameter. Type parameters do not refer to values, but to types. This is used in generic methods and classes, that allow (a certain) flexibility of the types used in the class, while staying statically typed.
Compare the generic list class List<T>. You have to pass a type parameter to enforce that you can't add instances of other types.
List<int> myList = new List<int>();
myList.Add(1);
myList.Add(2);
foreach(var item in myList)
{
// ...
}
Since myList has the type parameter int you and the compiler (and intellisense) know, that everything in myList is an int. Hence item will have the type int. On the other hand the following is not possible
myList.Add("FooBar");
since List<T>.Add has the signature void Add(T item) and creating a List<int> fixes the typeparameter T to int.
The second syntax
If Camera is a type this syntax is no valid C#. The compiler will throw the error
Camera is a type, which is not valid in the current context.
If GetComponentInParent would take a Type parameter (a parameter of the type Type, not a generic type parameter) you could call it like
GetComponentInParent(typeof(Camera))
but you will lose the merits of generics, i.e. the return type of GetComponentInParent won't be a Camera, but a less specialized type. If the components that can be returned by GetComponentInParent have no common ancestor, it might even return an object, whereas the generic version might have the signature
T GetComponentInParent<T>()
ich which case it would return the right type out of the box without the need to cast the result.
The argument inside the angle brackets is a type argument used for what is called "generics". The GetComponentsInParent method uses the C# generics functionality to have a different return type based on the generic type argument. You likely use this concept often without thinking too much about it when you are using lists. If I have a list of Camera objects, the way that I will create it is to say var cameras = new List<Camera>(); as opposed to var cameras = new List(Camera);.
The GetComponentsInParent method has a signature of T[] GetComponentsInParent<T>(). If is was to take a type in the parameter list instead of using generics, the signature would have to be object[] GetComponentsInParent(type T) or may GameObject[] GetComponentsInParent(type T) and it would be your code's responsibility to cast the returned array elements into the object that you actually need. Generics help us make these scenarios much cleaner.
For more information on generic types in C#, see https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/
Consider this code:
static void Main(string[] args)
{
var ints=new List<int> {10,11,22};
Something(ints);//Output:Count is:3
Something(new int[10]); //'System.Array' does not contain
// a definition for 'Count'
Console.ReadLine();
}
static void Something(ICollection collection)
{
dynamic dynamic = collection;
Console.WriteLine("Count is:{0}", dynamic.Count);
}
When pass a List all thing is ok. But when pass array and convert to dynamic i get this error:'System.Array' does not contain a definition for 'Count'.
I know what is my solution but i want to know why compiler has a this behaviors?
Something(new int[10]);
static void Something(ICollection collection)
{
//The dynamic keyword tells the compilier to look at the underlying information
//at runtime to determine the variable's type. In this case, the underlying
//information suggests that dynamic should be an array because the variable you
//passed in is an array. Then it'll try to call Array.Count.
dynamic dynamic = collection;
Console.WriteLine("Count is:{0}", dynamic.Count);
//If you check the type of variable, you'll see that it is an ICollection because
//that's what type this function expected. Then this code will try to call
//ICollection.Count
var variable = collection;
Console.WriteLine("Count is:{0}", variable.Count);
}
Now that we can understand why dynamic.Count is trying to call System.Array.Count. However, it's still unclear why Array.Count is not defined when Array implements System.Collections.ICollection which has a Count method. Array does in fact implement ICollection correctly, and it does have a Count method. However, consumers of Array.Count do not have permission to access the Count property without explicitly casting the Array to an ICollection. Array.Count is implemented with a pattern known as explicit interface implementation where Array.Count is explicitly implemented for ICollection. And you may only access the count method by casting your variable to an ICollection with this pattern. This is reflected in the docs for Array. Look for the "Explicit Interface Implementations" section.
var myArray = new int[10];
//Won't work because Array.Count is implemented with explicit interface implementation
//where the interface is ICollection
Console.Write(myArray.Count);
//Will work because we've casted the Array to an ICollection
Console.Write(((ICollection)myArray).Count);
Dynamic works internally using reflection. The array class does not have a property Count. It has a property Length which explicitly implements the ICollection property Count. This means that when you try to do the dynamic invocation, it fails, because it can not find a matching property.
My question for you would be why are you trying to use dynamic in this case -- you've already limited it to classes that support an interface, at that point you should be using the interface (which would work). At this point you're pretty much guaranteed to be able to get an enumerator and the count -- nothing else. If you need more, consider a better interface.
"I know what is my solution but i want to know why compiler has a this behaviors?"
For your question, as I know the compiler behavior is..
The compiler doesn't handle the dynamic type variable at compile-time, because the dynamic type is handled at the run-time. That's why that error appears.
If you want the compiler to handle this case, you can change your dynamic type into var type.
in a short words.. the dynamic type variable is not the compiler's responsibility.
It's because the name of the property is not Count, but rather System.Collections.ICollection.get_Count.
If you run
foreach (var item in typeof(int[]).GetMembers(BindingFlags.NonPublic |
BindingFlags.Public |
BindingFlags.Instance))
Console.WriteLine(item.Name);
you will get back
...
System.Collections.ICollection.get_Count
...
which is because the interface is implemented explicitly.
As at the link: http://msdn.microsoft.com/en-us/library/bb383973.aspx
...An implicitly typed local variable is strongly typed just as if you had declared the type yourself, but the compiler determines the type...
But I have such piece of code:
protected void Page_Load(object sender, EventArgs e)
{
if (Session["user"] == null) Response.Redirect("Default.aspx");
StringBuilder sb = new StringBuilder();
foreach (var item in Session)
{
sb.Append("Session Parameter: [");
sb.Append(item);
sb.Append("]<p />Guid Value: [");
sb.Append(Session[item] + "]");
}
Response.Write(sb.ToString());
}
I'm getting such error in Visual Studio:
Argument 1: cannot convert from 'object' to 'string' for the line:
sb.Append(Session[item] + "]");
But item is identifying at runtime as a string type as I looked in the debugger.
When I have read about var at msdn/in books I thought that var doesn't relate to RTTI-stuff. Compilers just changes the variable with this implicitly type on explicitly type like string, int etc at compile time.
Why did I catch such error?
A var declaration in C# is strongly typed but in this case you are dealing with a non-generic collection type in the value Session. This causes C# to choose object for the type of item and hence you get a later error trying to use item in a position that requires a string.
For non-generic collections you need to still explicitly type the iterator variable in a foreach block
foreach(string item in Session) {
...
}
"Why did I catch such error?" - because sb.Append(Session[item] + "]"); is expecting a string - so you need to cast the Session[item] to a string:
sb.Append(((string)Session[item]) + "]");
as per the MSDN article How to: Read Values from Session State.
In this case, Session is a HttpSessionState implements IEnumerable (though the same would happen if it implemented IEnumerable<object>), so your var gets mapped by the compiler into:
foreach (object item in Session)
{
In this case, this is likely IEnumerable, so you would need to specify the type explicitly in the foreach:
foreach (string item in Session)
{
This is allowed with foreach for non-generic IEnumerable collections.
As for your main question:
C# does var have a strong type?
Yes. In this case, the collection itself does not provide a strong type, so var uses System.Object.
You are correct, var is typed statically (i.e. at compile time). The static type of var, however, comes from the context, which lets the compiler derive the type of the var.
In case of Session which implements IEnumerable, the only type the compiler can derive is object, leading to the error that you describe.
Had Session implemented, say, IEnumerable<string>, the var in the loop would be equivalent to string, not to object.
In your example it comes down to what type of collection Session is. In this case it is a colleciton of objects so the compiler makes var item into object item.
As the other answers have indicated, Session's Item property (which is the default indexer) is of type object. That's why you're getting an object from the indexer - that IS its return type.
http://msdn.microsoft.com/en-us/library/k8s0kehy(v=vs.100).aspx
Its because object is a different type. Pretty much like why a derived class is a different from its base class (ex: class D : B{}). All class types inherit Object, values (struct) I believe do not. They need to be boxed in. The Session collection just says it has a bunch of objects and ANYTHING could be in there. The runtime doesn't try to check unless you ask it to (by typecast or is keyword). (More about the compiler below)
The var in that foreach is saying this variable (item) should be whatever type Session enumerator returns. For the most part specifying a different type there is like a typecast.
Just because something is a class or boxes (which means it is an object) doesn't mean the compiler has any idea of what the object actually is. In fact I think the standards suggest if something becomes an object the compiler should not auto convert it even if it knows what type it actually is (perhaps it knows from a few lines above)
A colleague pointed me to a strange case in C# (not so sure if this actually strange though).
Suppose you have a class Employee. If you want to create a Generic List<> of type Employee, you can simply do:
List<Employee> x = new List<Employee>;
I understand that I need to pass the Employee type to the Generic list so that it knows the required type information about Employee and generates methods that return and accept parameters that are compatible with Employee.
Now my question is, why isn't it possible to do the following?
Employee x = new Employee();
List<typeof(x)> list = new List<typeof(x)>();
Shouldn't this suffice the information required for List<> to know, in order to create a list? In other words, the type of x which is the type of Employee is now passed as a generic type parameter to List<>, which (as I used to believe) is the same as passing list the type name (in this case Employee).
I know that something like this is available in Java (using the .class) keyword on a variable.
I'm sure I AM missing something, so please, enlight me guys!
No, the equivalent of that isn't available in Java. You can't use "x.class" to get at the declared type of a variable.
Moreover, typeof(x) doesn't work in C# either to get the type of a variable - it returns a Type reference for the type name, e.g. typeof(string) will return a reference to the Type object associated with the System.String type. That's equivalent to using String.class in Java. (Note that again, that's applying .class to a type name, not a variable name.)
Java generics don't support anything like your final statement either. If you believe they do, please give a sample :)
What you can do in C# is use type inference to do what you want:
public static List<T> CreateListForSampleType<T>(T sample)
{
return new List<T>();
}
...
Employee x = new Employee();
var list = CreateListForSampleType(x);
Note that there's no reason why C# couldn't be extended to allow something like typeof(variablename) or List<typeof(variablename)> - it's all compile-time type information, after all. However, I can't see that it would meet the team's requirements for usefulness... there are other far more useful features I'd like to see first :)
The reason for this is that typeof() returns a type object, while you need a type name to initialize a list at compile-time.
One part of the answer is that the type of x is not available at compile time, i.e. it might be created using something like this:
Employee x = EmployeeFactory.NewEmployee("John Doe"); // Returns TraineeEmployee, Employee or ManagementEmployee;
List<typeof(x)> l = new List<typeof(x)> l(); // What type is it?
You can however create a List of a base class of what you want to store in the list (or even a List of "object"s).
typeof is used with class names. Use GetType() on an object, but only at runtime...
What you are missing - imho - is the difference between a static type reference at compile-time and a dinamyc type reference (via an instance of System.Type) at run-time.
typeof() and .GetType() give you the latter. (for types and instances, respectively)
I hope it makes clear.
Jon Skeet's code above is cool.
Can't find a reason to create an empty list since C# 3.0.
I generally create list instances only with a ToList method from an IEnumerable, which is in turn generated using a yield return, Concat(), Repeat(), Where() etc.
var list = CreateEmployees().ToList();
,
public IEnumerable<Employee> CreateEmployees()
{
yield return new Employee("Foo");
yield return new Employee("Bar");
}