Live data from Hacker News

What’s New in C# 7.0

blogs.msdn.microsoft.com

261–270 of 278 posts

Re: What’s New in C# 7.0

#261
post #258

Earlier quoted context omitted.

The most obvious approach would be to repurpose C++ syntax, since it's already been around for a while, and C# syntax is generally pretty close to C++ in many respects. So: public SomeMethod(bool b) -> (string name, int id) { ... } However, this may be undesirable due to confusion with => for lambdas and expression-bodied methods, especially in: public SomeMethod(bool b) -> (string name, int id) => ... : is the next…

I like the as syntax, it may conflict with casting though when used on variables. public SomeMethod(bool b) -> (string name, int id) { ... } When was this added to c++? I think I need to brush up on my lower level skills.

With regards to "as" conflicting, I think it shouldn't be a problem because the cast operator is binary. So if you already know that something is a declaration, the current token is the identifier that's being declared (or closing parenthesis of the parameter list), and the following token is "as", it cannot be a valid expression. Consider:

  var x as int;
"var" makes it a declaration, so "x" is the identifier, and "as" has to be the type specifier.

  var x = y as int;
"=" clearly separates the identifier from the initializer, and the latter is an expression, so "as" is the cast operator.

Similar reasoning applies to other places where the two can appear - function parameter list, return type, field and property declarations etc.

So far as I can tell, by the time we get to the point where "as" would be used to specify the type of the declared entity, we will already know that it's a declaration, and that the only other token that can follow in existing grammar is "=", "{", or "=>" (for variable and field initializers and default parameter values, property getters and setters, and lambdas and expression-bodied members, respectively), and none of these start an expression. In all other contexts, "as" would be an operator.

Re: What’s New in C# 7.0

#262
post #139

Earlier quoted context omitted.

Why not winforms? Very convenient, and mostly works in mono

WinForms is a dead end. It doesn't support Windows modern or Mac OS X, and it looks completely out of place on Linux.

On Windows it takes the style of whatever window theme you're using, what do you mean?

Re: What’s New in C# 7.0

#263

Earlier quoted context omitted.

While it's used for game development, I'm not convinced that it automatically means features should cater specifically to that audience. Compared to everything non-performance critical the language is used for, the performance crowd is dwarfed. I'm not against it, I personally have been in situations where it would have been useful, I'm just not convinced it's beneficial for the language to implement it.

I watched the NIAC talks yesterday and a quote (paraphrased) that I found interesting was: > Computation is innovated thanks to gamers. The same effect might benefit programming languages.

I think that is true. Where they seek to get that last bit of performance, they get creative and come up with new and better ways to do things. But I don't think C# is the language to do this, it lacks too many low level constructs.

Re: What’s New in C# 7.0

#264
post #171
post #168

I tend to avoid using switch/case for quite irrational reasons. The break statement just looks wrong and I can't layout the code in a way I find at all pleasing. I'll almost always use a sequence of else if conditions instead. In my ideal world, it'd look like... switch (x) { case 1: { /* ... */ } case 2: case 3: { /* ... */ } } And before anyone points it out, switch is the same as else if when the thing being compa…

Same here, plus I think the parent brakes are noise, can the compiler just know that the switch statement ends on last case?

Not with nested switch statements.

Re: What’s New in C# 7.0

#265

I still lack some kind of sum type that is always checked for exhaustiveness. When I'm creating a class hierarchy for a domain, 9 times out of 10 I'd rather have a simple sum type. When I have to make a class hierarchy in C# anyway (because there are no simple sum types). I'd like to be able to write something like this, and have this match/switch fail to compile if I add a new type of shape. match(shape) { case Rect…

I'm just going to pimp my library (github.com/mcintyre321/OneOf) which lets you pull this off in c#. I'm quite sad it's needed though, it would be great to have it in the language.

That's very neat, it should be pretty easy to sugar something like that into the compiler with no CLR changes.

Re: What’s New in C# 7.0

#266
post #259

Earlier quoted context omitted.

I have. If they were designed well then they are normally OK to maintain. If they were developed badly then they can take extra effort. Much like any other project in any other language. If you had an large old Vb6 project build using MVP pattern you would be laughing.

Much like any other project in any other language. Yes, you can write good or bad code in any language. But some languages have an endemic culture of not caring about good code. IME, VB is one of them, as is PHP. All the VB6 code I've seen has been a random mashup of databinding directly to the and click handling. The tools encouraged this approach.

I see your point to a degree. The tools made it easy for folks to get apps up and running quickly, RAD being a popular buzzword at the time. I don't think it's fair to say the culture was that of people who didn't care about the code.

I've certianly seen lot's of terrible VB6 code, but also lots of Vb6 apps that were better designed than some WinForms C# apps I've seen. I know this is all subjective.

Re: What’s New in C# 7.0

#267

Earlier quoted context omitted.

So I'm taking a look at this line: if(!memo.TryGetValue(rdr.GetInt32("student_id"), var out complete) memo.Add(new Student(rdr out complete).ID, complete); I think I understand your point, but I found this code really hard to read. You don't use the first var out complete in the TryGetValue, right? And the Student c'tor returns itself as an out parameter? If I understand you correctly, you like this because you don't…

Hi, thanks for taking the time to comment. > You don't use the first var out complete in the TryGetValue, right? There is only one complete variable; we declare it in TryGetValue, and it's definitely used in the last line of the while statement, but might first be used after the if statement. > And the Student c'tor returns itself as an out parameter? The Student constructor does not return itself as an out parameter…

Thank you, in turn, for replying. I'm of the opinion that out variables have little value if you have tuples and deconstruction, but I wanted to understand your point. I'm not sure I do, unfortunately. I'm little out of practice with C#, so bear with me.

Back to this code:

  if(!memo.TryGetValue(rdr.GetInt32("student_id"), var out complete)
    memo.Add(new Student(rdr out complete).ID, complete);
There's a parens missing on the end of the if, correct? Also I can't parse the student c'tor:

  new Student(rdr out complete)
I was assuming there is a comma in there somewhere. Does this compile? What does "out" do here? I thought you were getting a new out variable, but that's not correct since you wouldn't be able to name it the same in the same scope.

Also, I don't see how "complete" is ever non-null. If the ID isn't in the dictionary, then TryGetValue returns false and "complete" is null. Then you add the null "complete" to the dictionary, and throw away the Student object (which apparently does other side effects) once you have its id? If ID is in the dictionary, you get back what you inserted, which is still null.

And then you call .AddCourse on the possibly null reference? I'm lost.

Can you post the code again? Maybe I'm just missing something due to a syntax error.

Re: What’s New in C# 7.0

#268
post #129
post #52

Earlier quoted context omitted.

These tend to become cargo cult practices, "thou shalt never" that get in the way of those few places where they are useful.

I don't think so. The purpose of compiler warnings, lint-like tools, static analyzers and such is to bring developer attention to some block of code. Not 'Hey, you should not do it this way', but 'Hey, is this thing here as intended by you or just a typo or mistake?'. I like the way its implemented in Perl. Use 'use strict' by default, and guard the block where you really need something unusual by 'no strict somethin…

In C# one can always

  #pragma warning disable ${list of warnings}
and

  #pragma warning restore ${list of warnings}
https://msdn.microsoft.com/en-us/library/441722ys.aspx

Although frankly the list of warnings to disable and restore consists of warning numbers, not names, which is not super convenient.

Re: What’s New in C# 7.0

#269

Earlier quoted context omitted.

Hi, thanks for taking the time to comment. > You don't use the first var out complete in the TryGetValue, right? There is only one complete variable; we declare it in TryGetValue, and it's definitely used in the last line of the while statement, but might first be used after the if statement. > And the Student c'tor returns itself as an out parameter? The Student constructor does not return itself as an out parameter…

Thank you, in turn, for replying. I'm of the opinion that out variables have little value if you have tuples and deconstruction, but I wanted to understand your point. I'm not sure I do, unfortunately. I'm little out of practice with C#, so bear with me. Back to this code: if(!memo.TryGetValue(rdr.GetInt32("student_id"), var out complete) memo.Add(new Student(rdr out complete).ID, complete); There's a parens missing…

You're right, there were missing tokens; sorry about that.

Here is the equivalent C# 6 version of the code (i.e. something very similar to the pattern that I currently use):

        IEnumerable GetStudents(SqlCommand cmd)
	{
		var rdr = cmd.ExecuteReader();
		var memo = new Dictionary();

		while(rdr.Read())
		{
			Student.Completion completion; //this declaration will be unnecessary in C# 7
			
			if(!memo.TryGetValue(rdr.GetInt32("student_id"), out completion))
				memo.Add(new Student(rdr, out completion).ID, completion);
			
			completion.AddCourse(rdr); //completion is *guaranteed* to be non-null
		}
		
		return from kv in memo select kv.Value.Student;
	}
As you can see, it only differs from the C# 7 version by one line.

The first thing to note is that, per the C# spec, the `out` parameters of a method must be definitely assigned before the method returns[1]. It just so happens that the constructor of the `Student` class always creates a new `Completion` object and assigns it to the `out` parameter. Now, theoretically, an `out` parameter could be assigned a null reference (as in the case of TryGetValue), but in practice it's trivial to guarantee that it will be non-null (as in the case of our Student cstor).

In the line,

    memo.Add(new Student(rdr, out completion).ID, completion);
we first call the Student cstor, which assigns a non-null value to completion, so that by the time `memo.Add` is called, the completion variable is guaranteed to be non-null.

Also, `Student.Completion` is a class that is defined inside of the `Student` class. As such, it has access to all of Student's members (private and public). But, in order to do anything to an instance of Student, a Completion instance must have field which references that Student instance (unlike the case of Java's inner classes, which are a bit more powerful I think). That is why this line is possible:

     return from kv in memo select kv.Value.Student //`Value` is an instance of Student.Completion
Here is the basic definition of the Student class:

        sealed class Student
	{
		public int ID { get; } //this is a readonly property, meaning it can only be modified in a cstor
		public FullName { get; } //readonly property

		private List courses = new List(); 

                public IEnumerable Courses => from c in courses select c;
		
		public Student(IDataReader rdr,  out Completion completion)
		{
			ID = rdr.GetInt32("student_id");
			Fullname =  rdr.GetString("fullname");
			
			completion = new Completion(this);
		}
		
		public sealed class Completion
		{
			public Student Student { get; } //readonly property.
			
			public Completion(Student student)
			{
				this.Student =  student;
			}
			
			public void AddCourse(IDataReader rdr)
			{
				if(rdr.GetInt32("student_id) == Student.ID)
					student.courses.Add(new Course(rdr));
			}
		}
	}

 
Like I said in my earlier comment, my immediate goal was to be able to create a set of immutable objects with arbitrary nestings from the result of a single SQL query that may have an arbitrary number of joins (which is how we represent nesting relationally). The above example just has just one nested property, but I have production code in which objects have many more nested properties. For example, the `Course` class in the aforementioned example might have its own `Completion` class for adding `CourseAssignment` instances (e.g. select from Student left join Course on ... left join CourseAssignment on ...).

But, the really big idea is that I wanted an object-capability system[2][3]. Getting objects be immutable "almost everywhere"[4] is a nice side benefit.

[1] http://www.ecma-international.org/publications/files/ECMA-ST... (section 12.1.6)

[2] https://en.wikipedia.org/wiki/Object-capability_model

[3] https://www.youtube.com/watch?v=EGX2I31OhBE

[4] https://en.wikipedia.org/wiki/Almost_everywhere (in this case "almost everywhere" is with respect to the set of all possible execution paths).

Re: What’s New in C# 7.0

#270
post #171

Earlier quoted context omitted.

Same here, plus I think the parent brakes are noise, can the compiler just know that the switch statement ends on last case?

Not with nested switch statements.

...I don't want to ever have to work with your code.
Post reply on HN