The Netduino forums have been replaced by new forums at community.wildernesslabs.co.
This site has been preserved for archival purposes only
and the ability to make new accounts or posts has been turned off.
Tell me, if you were a student learning C#, don't you wonder why these lines are not legal?
I guess there are a few principles behind your statement that I find interesting, namely:
The proper coding style is the one which is readily understood by a student learning C#
Students learning C# tend to be confused by language features that do not work in all contexts
For that reason, such features should be avoided
Is that a fair way to summarize your point? If so, it seems to rule out other language features as well. Does the code below demonstrate that one should never use field initializers, ref, or out? If not, what is different about var?
namespace NetduinoApplication3 {
public class Program {
private int a=5;
private int b=a+1;
private ref int c;
private out int d;
}
}
First off, very good move the start of a new thread.
Corey, it seems you are going to convince me!
For both of us the "var" is not a problem, I think. I am *not* a computer scientist, so I can't blame a deep choice of the C# engineering staff.
What I've seen is that many people, moving their first steps in C#, find strange that they may use "var" inside function, but not elsewhere. They are wondering why?
On the same thread we are just left, two readers posted the same question...I guess this is not a coincidence.
Anyway, I want to make another example, that I have found confusing. (Note: here is *me* and nobody else).
Let's take a couple of things of Java:
- the native types, such a boolean, int, etc.
- the "protected" accessor
I never programmed in Java, and I switched years ago directly from VB6 to C#.
For me was a shock knowing that in Java there are two kinds of "value-types": the raw-one and the object-one. What's the sense? Why can be only the "object" like C#?
It is absolutely a nonsense having an int of type "int" and another int of type "Integer": that's pure confusion.
About the "protected" keyword, I know that both in C++ and C# has the same meaning.
Why not in Java? In Java is much more the "internal" of C#. I may understand that Java is born before C#, but C++ is even older!
So, I personally may total agree with you, but I also think the "var" *could* be better (e.g. giving the possibility to use it everywhere)
After all, your example of "ref" and "out" pointed me another "weakness" of the C# syntax.
Consider this:
struct S
{
public int A;
public int B;
}
class C
{
void Main()
{
S[] sa = new S[10];
DoWork(ref sa[3]);
DontWork(ref sa[3]);
}
void DoWork(ref S s)
{
s.A = ...
s.B = ...
}
void DontWork(S s)
{
s.A = ...
s.B = ...
}
}
Nothing special.
I have an array of S and I'd like to write the fields of a cell.
I must use the "ref" otherwise I'll work over a copy that is be lost coming out the function.
But, what if I do *NOT* want a function (e.g inline)?
void Main()
{
S[] sa = new SA[10];
sa[3].A = 123;
var s = sa[3];
s.B = 456; //won't work!
}
I guess there's no way to do without a method (or unsafe coding).
I realized that because I wanted a performing way to fill up the fields of a struct in an array for the Netduino. Since the "ref" trick is highly performing than the indexing (even faster than the unsafe ways), the only way is to do with a custom function.
I really can't find another way...
In the sense, the "ref" you've proposed is not so bad!
Finally, I'd like to make you some questions about the Fluent framework, but I'll do them on a proper thread.
Thanks
Bye!
Biggest fault of Netduino? It runs by electricity.
A thread which will always have emotions running high and one where we will never see a right answer.
The proper coding style is the one which is readily understood by a student learning C#
This is a mythical entity which can only be found with unicorns. When two or more programmers get togther they will always disagree on the correct coding style.
Is that a fair way to summarize your point? If so, it seems to rule out other language features as well. Does the code below demonstrate that one should never use field initializers, ref, or out? If not, what is different about var?
I must admit, I personally have no problem with ref or out but I do have a problem with var, namely, it allows the compiler to make decisions for me and those decisions may not be correct (such as making a variable an int when I wanted an unsigned short). I also feel that using var is an easy way out of understanding what is actually happening. The most common use I have seen is for the results variable definition in a LINQ query. I'd like to think that my team understand the type of the result sets being returned but I suspect that many new programmers do not and that to me is a bad thing.
So, having lit the blue torch paper I shall retire to a safe distance whilst someone calls for the fire brigade.
I never programmed in Java, and I switched years ago directly from VB6 to C#.
For me was a shock knowing that in Java there are two kinds of "value-types": the raw-one and the object-one. What's the sense? Why can be only the "object" like C#?
Strictly speaking I think you will find that C# has the concept of both a value type (simple types like int, enums, structs and nullable types) and a reference type (classes, interfaces, arrays and delegates). This is why you need boxing in the following example:
private void func(object o)
{
// Do stuff with o
}
int i;
i = 100;
func(i);
Here the integer i is boxed into a new object and not simply cast to an object.
I must admit, I personally have no problem with ref or out but I do have a problem with var, namely, it allows the compiler to make decisions for me and those decisions may not be correct ...
Explore the functional programming space (specifically the concept of type inference) and you may come away with an appreciation of var.
Hmmm...I admit to be not so deep in this detail, but...
Let's write:
int i = 123;
Int32 j = i; //boxed?
Print(i.ToString()); //boxed?
As far I know the C# "int" is the alias for the System.Int32 of the .Net. By assigning the "native" 123 to the "Int32 object" do I implicitly box it?
That's new for me.
Mario
Biggest fault of Netduino? It runs by electricity.
By assigning the "native" 123 to the "Int32 object" do I implicitly box it?
Definitely not. System.Int32 is the name of the type in the CLR. C# helpfully provides the keyword int as a shortcut for typing System.Int32. However they are identical and interchangeable in every possible way. (The same holds for many other keywords, such as System.String vs string)
Neither of your two "boxed?" lines boxes the int. However this one will
As far I know the C# "int" is the alias for the System.Int32 of the .Net. By assigning the "native" 123 to the "Int32 object" do I implicitly box it?
No, I've just checked with the IL-disassembler: there's no boxing...as I suspected.ù
However there is boxing toward an object, as Mark shown.
So, I may declare a "number" and *without any boxing* using as an object (methods, props, etc).
That's *not* what happens with Java (e.g. integer)
An int is a number; an Integer is a pointer that can reference an object that contains a number.
/* Java 1.4 syntax */
public static void main(String[] args) {
// add two ints
int a = 1;
int b = 1;
int c = a + b;
System.out.println(c);
System.out.println(a == B);
// add two Integers
Integer x = new Integer(1);
Integer y = new Integer(1);
Integer z = new Integer(x.intValue() + y.intValue());
System.out.println(z);
System.out.println(x == y); // comparing references to different objects
}
This is a mythical entity which can only be found with unicorns. When two or more programmers get togther they will always disagree on the correct coding style.
I agree that developers will disagree on correct coding styles, but there are practices that are easier to read than others. I'm not talking about people who are learning the C# language so much as the developer who is trying to understand what your code is doing. The easier it is to glance at a single line of code and see exactly what it does, the less time a developer will spend going over the entire class to understand it. Again, these kinds of things are generally more important when the project in question is very large. Even when the code is small, though, it can help developers of varying skill levels if the code is easier to read, especially when the code is intended to be shared, such as the libraries and examples we see here.
I must admit, I personally have no problem with ref or out but I do have a problem with var,
All three are useful in some places. I think the danger that they can easily be overused, which makes for sloppy code. Especially in the case of ref and out, this means more interrelationships (i.e. a change in one place of code is more likely to affect or even break code somewhere else).
... it allows the compiler to make decisions for me and those decisions may not be correct (such as making a variable an int when I wanted an unsigned short).
I agree. It is much easier to find bugs if they show up as compile errors (type mismatch) than if they show up as runtime errors (var not defined as the type you expect).
... The most common use I have seen is for the results variable definition in a LINQ query. I'd like to think that my team understand the type of the result sets being returned but I suspect that many new programmers do not and that to me is a bad thing.
I agree that understanding the type is important, but using structures like anonymous types becomes much more difficult without having var available.
Most programming languages have elements that provide significant power to the developer, like macros in C++ or anonymous types in C#. These tools should be used carefully because tracking down the problems they sometimes cause can be extremely difficult, even for an experienced developer.
I agree. It is much easier to find bugs if they show up as compile errors (type mismatch) than if they show up as runtime errors (var not defined as the type you expect).
The easier it is to glance at a single line of code and see exactly what it does, the less time a developer will spend going over the entire class to understand it.
Totally agree.
Especially in the case of ref and out, this means more interrelationships
I know what you mean, the purist in me says that ref and out should not be used as they break some of the fundemental tenents of OOP. However, having spent time working with .NET 1.1 through to .NET 4, the TryParse pattern added to DateTime etc. made our code so much easier to read and understand.
It is absolutely a nonsense having an int of type "int" and another int of type "Integer": that's pure confusion.
I think this is kind of a deep question, and I don't have a perfect answer. But I can give you some ideas.
It would indeed be simpler to have a different language where everything was a reference type (and such languages do exist). However, one major challenge is performance. Creating and manipulating a value-type integer can be as cheap as a single CPU instruction, whereas allocating a reference type is going to take tens or maybe hundreds of instructions. Another serious disadvantage is memory overhead. A heap-allocated object costs you at the very least an additional pointer (and the corresponding level of indirection at runtime); additionally your implementation may (it certainly does in .NET) add an object header in front of each object. So the net cost of your little integer might turn out to be quite high once you heap-allocate it. So by making your language "cleaner" you have baked in some major performance problems. Rather than crack this nut, the language designers for Java and C# decided that they were going to have to make the distinction explicit.
I am pretty ignorant of the state of the art in programming languages. It's possible that the most modern and advanced functional languages (like Haskell?) use techniques so that they don't have these problems, but I don't really know either way.
So anyway, Java decided to make the distinction explicit with int vs Integer. C# provides a more elegant solution which is somewhat more convenient for the programmer and, thankfully, not part of the type system. (That is, "int" and "boxed int" are the same type, e.g. with respect to GetType())
Personally I'd prefer a programming language where the distinction is entirely rather than partially hidden from the programmer, but I don't know what that solution would look like.
Your comment about structs is interesting. I believe the feature you want is a limitation of the C# language and not the CLR. That is, I believe if there was a compiler for the following C#-like code, it would pass the code verifier and the CLR would run it:
void Main()
{
S[] sa = new SA[10];
sa[3].A = 123;
ref S s = sa[3]; //hypothetical syntax -- doesn't work in C#
s.B = 456;
}
Sometime soon I will try to confirm this hypothesis by generating the MSIL code corresponding to this and see if I can get the system to assemble it (and more importantly, for it to pass peverify.exe)
You're correct, but it can introduce problems you won't see until runtime. For example, if the compiler decides the type is byte, but you expect int32, there will be no compiler error, just an overflow when you hit the bigger values.
Your comment about structs is interesting. I believe the feature you want is a limitation of the C# language and not the CLR. That is, I believe if there was a compiler for the following C#-like code, it would pass the code verifier and the CLR would run it:
void Main()
{
S[] sa = new SA[10];
sa[3].A = 123;
ref S s = sa[3]; //hypothetical syntax -- doesn't work in C#
s.B = 456;
}
Sometime soon I will try to confirm this hypothesis by generating the MSIL code corresponding to this and see if I can get the system to assemble it (and more importantly, for it to pass peverify.exe)
Use of ref and out is more meaningful when talking about method arguments. Declaring a local variable as ref or out doesn't mean anything, as far as I know.
You're correct, but it can introduce problems you won't see until runtime. For example, if the compiler decides the type is byte, but you expect int32, there will be no compiler error, just an overflow when you hit the bigger values.
That's not true.
The problem usually arises because none activates the checking options on numerical operations.
By default, you implicitly accept that operations on numbers could roll-over, overflows, etc.
Declaring a local variable as ref or out doesn't mean anything, as far as I know.
It does not matter to implement that function, but it would solve the problem I have posted above.
On a PC you won't care about it, but on a small device where the performance is important, the ability to access the field of a structure as fast as possible I think it's important.
Actually there's no way in C#, unless you create a custom function just to do it.
Upon this point (that I raised), I'd agree with Eric Lippert of MS, that often explains us the ratio of benefits/costs of some feature.
Cheers
Mario
Biggest fault of Netduino? It runs by electricity.
Sometime soon I will try to confirm this hypothesis by generating the MSIL code corresponding to this and see if I can get the system to assemble it (and more importantly, for it to pass peverify.exe)
Confirmed--I'm sure vernarim will appreciate this :-)
using System;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
namespace ConsoleApplication13 {
public class Program {
public static void Main() {
try {
var type=GenerateCode();
var m=type.GetMethod("MyMethod");
var data=new MyStruct[10];
m.Invoke(null, new object[] {data});
Debug.Print("data[3].A={0}, data[3].B={1}", data[3].A, data[3].B);
} catch(Exception e) {
Debug.Print("oops: "+e);
}
}
//Generate the following type:
//
//public class MyType {
// public static void MyMethod(MyStruct[] items) {
// ref MyStruct myRef=items[3]; //C#-like code
// myRef.A=12;
// myRef.B=13;
// }
//}
private static Type GenerateCode() {
var ab=AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("myAssembly"),
AssemblyBuilderAccess.RunAndSave);
var myModule=ab.DefineDynamicModule("myModule", "myModule.dll", true);
var myType=myModule.DefineType("MyType");
var method=myType.DefineMethod("MyMethod", MethodAttributes.Public|MethodAttributes.Static, typeof(void), new[]{typeof(MyStruct[])});
var fieldForA=typeof(MyStruct).GetField("A");
var fieldForB=typeof(MyStruct).GetField("B");
var g=method.GetILGenerator();
//ref MyStruct myRef=items[3]; //C#-like code
var myRef=g.DeclareLocal(typeof(MyStruct).MakeByRefType());
g.Emit(OpCodes.Ldarg_0);
g.Emit(OpCodes.Ldc_I4_3);
g.Emit(OpCodes.Ldelema, typeof(MyStruct));
g.Emit(OpCodes.Stloc, myRef);
//myRef.A=12;
g.Emit(OpCodes.Ldloc, myRef);
g.Emit(OpCodes.Ldc_I4, 12);
g.Emit(OpCodes.Stfld, fieldForA);
//myRef.B=13;
g.Emit(OpCodes.Ldloc, myRef);
g.Emit(OpCodes.Ldc_I4, 13);
g.Emit(OpCodes.Stfld, fieldForB);
g.Emit(OpCodes.Ret);
var theType=myType.CreateType();
ab.Save("myAssembly.dll");
return theType;
}
}
public struct MyStruct {
public int A;
public int B;
}
}
You're correct, but it can introduce problems you won't see until runtime. For example, if the compiler decides the type is byte, but you expect int32, there will be no compiler error, just an overflow when you hit the bigger values.
public class Program
{
public static void Main()
{
var count = GetValue();
count += 200;
}
public static byte GetValue()
{
return 100;
}
}
Let's assume that GetValue is actually buried somewhere in another file. If the developer is expecting that GetValue returns an int, he won't expect to see count = 44 after that second line.
If count is explicitly defined as an int, it will behave as the developer expects (implicit cast to a larger type).
Also, if the types were reversed (GetValue returns an int, but count is defined as a byte), you'll see the compiler error "Cannot implicitly convert type 'int' to 'byte'. An explicit conversion exists (are you missing a cast?)". (Yes, I know that declaring count as var here won't make a difference to the compiler, as it already knows to use int.)
That's not true.
The problem usually arises because none activates the checking options on numerical operations.
By default, you implicitly accept that operations on numbers could roll-over, overflows, etc.
Yes, turning on overflow checking would help find the problem. Is this available on Compact Framework projects? I can't find it.
Also, in at least some implementations (if not all), this is still not a compile-time check, but a runtime check, IIRC.
It does not matter to implement that function, but it would solve the problem I have posted above.
On a PC you won't care about it, but on a small device where the performance is important, the ability to access the field of a structure as fast as possible I think it's important.
Actually there's no way in C#, unless you create a custom function just to do it.
Upon this point (that I raised), I'd agree with Eric Lippert of MS, that often explains us the ratio of benefits/costs of some feature.
I'm not sure what you're getting at. The code example you gave does work (with a few fixes).
class SA
{
public int A { get; set; }
public int B { get; set; }
public static void Method()
{
SA[] sa = new SA[10];
sa[3] = new SA();
sa[3].A = 123;
var s = sa[3];
s.B = 456; //won't work!
}
}
If you mean accessing an object without the "unboxing" you get with a reference, you can make the declaration using struct instead of class (DateTime is an example).