coding style discussion
#21
Posted 15 March 2011 - 07:27 PM
#22
Posted 15 March 2011 - 07:43 PM
I'm not sure what you're getting at. The code example you gave does work (with a few fixes).
I think by changing 'struct' to 'class' you have redefined the question and sidestepped the whole point. You won't be able to get your code to work with 'struct'.
If it helps, here is a self-contained program. The goal is to set the values A and B of array[3] in-place
- without disturbing the value of C
- without copying a whole new struct
- only referring to array[3] inside Func2() once
- without making any function calls from Func2
Func1 does this, but uses a function call.
Func2 does this, but mentions array[3] twice
You will not be able to rewrite Func2 in a way consistent with the above rules (Redefining MyStruct is beside the point). As I showed above, it however is possible using MSIL, and therefore possible in some hypothetical alternate or future language)
using System; using System.Diagnostics; namespace ConsoleApplication13 { public class Program { public static void Main() { var array=new MyStruct[10]; array[3].C=555; Func1(array); ShowResults(array); array=new MyStruct[10]; array[3].C=666; Func2(array); ShowResults(array); } private static void Func1(MyStruct[] array) { Func1Helper(ref array[3]); } private static void Func1Helper(ref MyStruct s) { s.A=13; s.B=14; } private static void Func2(MyStruct[] array) { array[3].A=13; array[3].B=14; } private static void ShowResults(MyStruct[] array) { Debug.Print("array[3]="+array[3]); } } public struct MyStruct { public int A; public int B; public int C; public override string ToString() { return string.Format("A={0}, B={1}, C={2}", A, B, C); } } }
#23
Posted 15 March 2011 - 07:59 PM
I think by changing 'struct' to 'class' you have redefined the question and sidestepped the whole point. You won't be able to get your code to work with 'struct'.
If it helps, here is a self-contained program. The goal is to set the values A and B of array[3] in-place
- without disturbing the value of C
- without copying a whole new struct
- only referring to array[3] inside Func2() once
- without making any function calls from Func2
I'm still not sure I understand what you're getting at. Using an interface will satisfy the requirements you listed, I think, but the structs will be boxed. If protecting the value of C is what's important, than either interfaces or better encapsulation looks like a good solution.
public static void Func2(ABOnly value) { value.A = 13; value.B = 14; } public static void Func3(MyStruct[] array) { ABOnly local = array[3]; local.A = 13; local.B = 14; } public interface ABOnly { int A; int B; } public struct MyStruct : ABOnly { public int A; public int B; public int C; }
If you mean accessing the struct without boxing (for performance reasons), I can see your point, but the performance hit from boxing is negligible compared to many other things you find in managed programming and algorithms in general. Unless this is a thought exercise, you should keep in mind that performance tuning is best when surgically applied to specific problem areas.
#24
Posted 15 March 2011 - 08:14 PM
I'm still not sure I understand what you're getting at. Using an interface will satisfy the requirements you listed, I think,
The interface approach does not work, and in fact your code does not even compile. I think it would help you understand what I am getting at if you try to compile and run your proposed solution using the test program I painstakingly provided. If the output matches what is expected that would be a very interesting result. If not, you can think about alternate approaches. This will help you verify whether your ideas work without having to post proposals to this forum. I don't think this will take very much additional effort, and additionally I think it will bring clarity to the discussion.
#25
Posted 15 March 2011 - 08:32 PM
The interface approach does not work, and in fact your code does not even compile. I think it would help you understand what I am getting at if you try to compile and run your proposed solution using the test program I painstakingly provided. If the output matches what is expected that would be a very interesting result. If not, you can think about alternate approaches. This will help you verify whether your ideas work without having to post proposals to this forum. I don't think this will take very much additional effort, and additionally I think it will bring clarity to the discussion.
Sorry about the compiler errors, but those are easily fixed:
public static void Func2(ABOnly value) { value.A = 13; value.B = 14; } public static void Func3(MyStruct[] array) { ABOnly local = array[3]; local.A = 13; local.B = 14; } public interface ABOnly { int A { get; set; } int B { get; set; } } public struct MyStruct : ABOnly { public int A { get; set; } public int B { get; set; } public int C { get; set; } }
It's better encapsulation to use properties instead of fields, anyway.
More importantly, does this solve the problem?
I'm sorry I don't have the time to retrofit my example into yours. I'm posting from work and they expect me to get stuff done, sometimes.
#26
Posted 15 March 2011 - 08:34 PM
More importantly, does this solve the problem?
Nope.
#27
Posted 16 March 2011 - 05:58 AM
#28
Posted 16 March 2011 - 12:45 PM
Fascinating. I'm surprised that the unsafe code was slower. I would have thought it would be fine. Can you post the code you wrote? Was it as simple as this:I have also tried by using some "unsafe" code, but it seems a bit slower (!) than this one:
void TimerTick(object state) { unsafe { Sample* sp=&_samples[_index]; sp->An0=_an0.Read(); sp->An1=_an1.Read(); sp->An2=_an2.Read(); sp->An3=_an3.Read(); } }
But in any case, I'm a little surprised. I would have assumed that the details of this routine are actually minor compared to (1) your event handling overhead (the mechanism that fires the TimerTick in the first place) and (2) the cost of anX.Read(). Is that not the case?
Edited by Corey Kosak, 16 March 2011 - 12:46 PM.
#29
Posted 16 March 2011 - 12:55 PM
void TimerTick(object state) { unsafe { int* sp=&_samples[_index].An0; *sp++=_an0.Read(); *sp++=_an1.Read(); *sp++=_an2.Read(); *sp=_an3.Read(); } }
and likewise I'd like to know if you have been able to get the so-called "fixed buffer" support to work in NETMF, which would allocate an array inline, (I haven't gotten it to work on NETMF):
private unsafe struct Sample { public fixed int An0[4]; }
#30
Posted 16 March 2011 - 01:29 PM
http://stackoverflow...o-improve-speed
Isn't a good choice: has the same speed of the safer way and it's dangerous too.
Anyway the Netduino world is driving me crazy. I *must* use some pattern to improve speed and sparing memory, but I'm also realizing all the beauty of C# is losing in favor of C++ patterns.
I'm a bit scared about using structs, because often are very tricky.
Read this:
http://blogs.msdn.co...ly-structs.aspx
At this point, for my problem, I see only three choices:
- custom hardware
- Fluent interop
- custom driver C++
I'll post some considerations about Fluent on a separate thread.
The C++ driver is the worst case, because I don't know C++ and I would not even recompile firmware. I have not experiences of gcc or similar, so the probability to hang my pc is near 100%.
For me is relatively easy to play with the hardware, so maybe...
Bah!...
#31
Posted 16 March 2011 - 02:59 PM
Anyway the Netduino world is driving me crazy.
I feel your pain. And also, you're right, my code is broken because it doesn't use "fixed" on the pointer. Oops Well, that's easily fixed, but if it doesn't help very much it doesn't matter. Anyway, I'll address your fluent questions on the other thread.
Edited by Corey Kosak, 16 March 2011 - 03:29 PM.
#32
Posted 16 March 2011 - 03:59 PM
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.)
Looks to me that the caller of GetValue() made an error (when declaring his local variable), not that var assumed the wrong type returned from GetValue().
#33
Posted 16 March 2011 - 07:32 PM
Looks to me that the caller of GetValue() made an error (when declaring his local variable), not that var assumed the wrong type returned from GetValue().
My point was that error (incorrect assumption) doesn't show up as a compile error in this case, where if the developer used an explicit type instead of var, there would be either a compile error or, in this case, no problem at all.
#34
Posted 16 March 2011 - 11:59 PM
Do yourself a favor: Buy a copy of Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition)
http://www.amazon.co...BD3Q166WETW2H4F
Read it, and apply those patterns.
Also, I highly despise the use of "var" in code. Just felt like I should say that....
#35
Posted 17 March 2011 - 12:44 AM
#36
Posted 25 March 2011 - 07:53 AM
#37
Posted 25 March 2011 - 12:53 PM
I'd like to respond to this, but I'm finding it difficult to do so because I'm not sure what you mean by "abomination". I think think of four kinds of situations involving 'var':As a experienced pro programmer, I'd like to join the group that despises the "var" keyword. It might be compile time typesafe, but it's still an abomination.
(Among others it makes the code less readable I think)
- Situations where it is absolutely required (there is no way to avoid it)
- Where it can be avoided if one is willing to bring in (likely irrelevant) details from the called routine
- Where one would otherwise type literally the same sequence of characters twice
- (other situations)
Examples for 1-3:
Item 1:
IEnumerable<People> people=GetPeople(); var peopleWithSameNames=from person in people group person by new {person.LastName, person.FirstName} into g select new {g.Key.LastName, g.Key.FirstName, Count=g.Count()};
Items 2 and 3:
static void Main(string[] args) { Dictionary<Tuple<string,string>, List<int>> dict1=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 Dictionary<Tuple<string,string>, List<int>> dict2=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 Dictionary<Tuple<string, string>, List<int>>.KeyCollection dict1Keys=dict1.Keys; //item 2 Dictionary<Tuple<string, string>, List<int>>.KeyCollection dict2Keys=dict1.Keys; //item 2 IEnumerable<Tuple<string, string>> allKeys=dict1Keys.Concat(dict2.Keys); }
In a world where var is not despised, this looks like:
static void Main(string[] args) { var dict1=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 var dict2=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 var dict1Keys=dict1.Keys; //item 2 var dict2Keys=dict1.Keys; //item 2 var allKeys=dict1Keys.Concat(dict2.Keys); }
#38
Posted 25 March 2011 - 01:13 PM
Item 1:
IEnumerable<People> people=GetPeople(); var peopleWithSameNames=from person in people group person by new {person.LastName, person.FirstName} into g select new {g.Key.LastName, g.Key.FirstName, Count=g.Count()};
I believe that this can be avoided by creating a new class to hold the names and create a new instance of the class. That way the var can be replaced by an IEnumerable of the new class.
static void Main(string[] args) { Dictionary<Tuple<string,string>, List<int>> dict1=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 Dictionary<Tuple<string,string>, List<int>> dict2=new Dictionary<Tuple<string, string>, List<int>>(); //item 3 Dictionary<Tuple<string, string>, List<int>>.KeyCollection dict1Keys=dict1.Keys; //item 2 Dictionary<Tuple<string, string>, List<int>>.KeyCollection dict2Keys=dict1.Keys; //item 2 IEnumerable<Tuple<string, string>> allKeys=dict1Keys.Concat(dict2.Keys); }
In many of the companies I have worked for this breaks local coding standards as we were not allowed to declare a variable and instantiate it on the same line. I think the idea was to leave space for a comment on the purpose of the variable if it's name or use was not clear.
I'm on the anti-var camp and so far the only argument I have heard for it which may convince me of its value is the functional programming argument - and this is something I would need to do further research on before I could make a comment with any authority.
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
#39
Posted 25 March 2011 - 02:09 PM
That's correct -- so long as it has a proper implementation of Equals() and GetHashCode(), which turns out to be a quite a bit of typing. You have to be a bit of an expert just to get .Equals() right.I believe that this can be avoided by creating a new class to hold the names and create a new instance of the class. That way the var can be replaced by an IEnumerable of the new class.
Then if you need to change the class, you need to decide whether you are its only user (in which case you can modify it) or whether there are other parts of your codebase relying on this "helper" class (in which case you need to make a new one). And then, surely your coding style rules say that you should get rid of classes that are no longer used, so you may need to delete old ones. It would be interesting to hear your/your company's take on this argument, as I would assume their interest is in minimizing the amount of code they need to test and maintain. i.e. why not let the compiler automatically supply these classes and their methods rather than put your programmers through all this busywork?
In other words, there's no creativity (and therefore no value-add) involved in writing such classes (it's all boilerplate), but there certainly is plenty of pain on the downside when one makes a mistake and gets them wrong.
I'm pretty surprised, as this would open the door to uninitialized variable bugs in C/C++. The compiler won't allow such bugs in C#/Java but still I don't see why you'd want there to be a gap between a variable's declaration and its initialization. The desire to leave space for commenting does not strike me as particularly compelling, as there are lots of ways to format code which allow for copious commenting.In many of the companies I have worked for this breaks local coding standards as we were not allowed to declare a variable and instantiate it on the same line
Just the other night as I was trying to write my stack-machine-to-register-machine translator for SimpleNGEN, I found myself writing this line:
var incomingStacks=new Dictionary<ControlFlowGraphNode<Labeled<StackOperation>>, MyNode<AbstractRegister>>();I think I have difficulty respecting the IT manager who would insist that this is better as
Dictionary<ControlFlowGraphNode<Labeled<StackOperation>>, MyNode<AbstractRegister>> incomingStacks; incomingStacks=new Dictionary<ControlFlowGraphNode<Labeled<StackOperation>>, MyNode<AbstractRegister>>();I mean, ugh, really? Does that clarify or obfuscate?
#40
Posted 25 March 2011 - 02:30 PM
Just FYI, a couple of years ago, I have found this document. I think it is very well done, even you may don't agree though.
http://csharpguidelines.codeplex.com/
In particular for the "var" question, it says (page 15):
Which I agree at all, even my habit is to use "var" often.Only use var when the type is very obvious
Only use var as the result of a LINQ query, or if the type is very obvious from the same statement and using it would improve readability.
Don't
var i = 3; // what type? int? uint? float? var imyfoo = MyFactoryMethod.Create("arg"); // Not obvious what base-class or // interface to expect. Also difficult // to refactor if you can't search for // the classDo:
var q = from order in orders where order.Items > 10 and order.TotalValue > 1000; var repository = new RepositoryFactory.Get<IOrderRepository>(); var list = new ReadOnlyCollection<string>();In all of three above examples it is clear what type to expect.
All that sounds much more like "the signal says don't run over 50KM/h, but there is none around, so I'll run faster".
I think every developer should adapt the most reasonable rules can do to make the code readable and easily to manage.
0 user(s) are reading this topic
0 members, 0 guests, 0 anonymous users