Netduino home hardware projects downloads community

Jump to content


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.
Photo

coding style discussion


  • Please log in to reply
45 replies to this topic

#1 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 15 March 2011 - 01:17 PM

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;
  }
}


#2 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 15 March 2011 - 02:25 PM

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.

#3 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 15 March 2011 - 02:40 PM

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.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#4 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 15 March 2011 - 03:10 PM

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.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#5 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 15 March 2011 - 03:24 PM


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.

#6 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 15 March 2011 - 03:27 PM

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.

#7 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 15 March 2011 - 03:32 PM

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

object j=i;


#8 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 15 March 2011 - 03:40 PM

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
  }
http://illegalargume...us-integer.html

Mario
Biggest fault of Netduino? It runs by electricity.

#9 Foozinator

Foozinator

    Member

  • Members
  • PipPip
  • 16 posts

Posted 15 March 2011 - 04:10 PM

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.

#10 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 15 March 2011 - 04:37 PM

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).


var is not resolved at runtime.

#11 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 15 March 2011 - 04:38 PM

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.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#12 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 15 March 2011 - 04:44 PM

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)

#13 Foozinator

Foozinator

    Member

  • Members
  • PipPip
  • 16 posts

Posted 15 March 2011 - 04:50 PM

var is not resolved at runtime.


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.

#14 Foozinator

Foozinator

    Member

  • Members
  • PipPip
  • 16 posts

Posted 15 March 2011 - 04:57 PM

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.


void Main()
{
   int count = 0;

   CopyOf( count );
   RefTo( ref count );

   Console.WriteLine( "count = " + count.ToString() );
}

void CopyOf( int count )
{
   count ++;
}

void RefTo( ref int count )
{
   count ++;
}

The TryParse example mentioned above is a good use of out.

#15 Mario Vernari

Mario Vernari

    Advanced Member

  • Members
  • PipPipPip
  • 1768 posts
  • LocationVenezia, Italia

Posted 15 March 2011 - 05:08 PM

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.

#16 Nevyn

Nevyn

    Advanced Member

  • Members
  • PipPipPip
  • 1072 posts
  • LocationNorth Yorkshire, UK

Posted 15 March 2011 - 05:12 PM

Declaring a local variable as ref or out doesn't mean anything, as far as I know.


Unless I've missed something you can't do that as it's not allowed in the syntax - ref can only be used for parameters as it has no meaning locally.

Regards,
Mark

To be or not to be = 0xFF

 

Blogging about Netduino, .NET, STM8S and STM32 and generally waffling on about life

Follow @nevynuk on Twitter


#17 Corey Kosak

Corey Kosak

    Advanced Member

  • Members
  • PipPipPip
  • 276 posts
  • LocationHoboken, NJ

Posted 15 March 2011 - 05:23 PM

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;
  }
}

Output:
data[3].A=12, data[3].B=13

Edited by Corey Kosak, 15 March 2011 - 06:22 PM.


#18 AlfredBr

AlfredBr

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationConnecticut, USA

Posted 15 March 2011 - 05:57 PM

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.

Can you post an example?

#19 Foozinator

Foozinator

    Member

  • Members
  • PipPip
  • 16 posts

Posted 15 March 2011 - 06:59 PM

Can you post an example?


	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.)

#20 Foozinator

Foozinator

    Member

  • Members
  • PipPip
  • 16 posts

Posted 15 March 2011 - 07:16 PM

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).

EDIT: Corrected the spelling of "struct"




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.