-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C# Design Notes for Mar 24, 2015 #1898
Comments
Have you given any consideration to nested methods? |
@paulomorgado Yes, but that hasn't been assigned a color yet. |
A question- at what point in the process do you start nailing down the designs of some of the features proposed thus far? It seems like the design meetings currently jump from one feature to the next- it seems to me that trying to design features like that runs the risk of circular discussions as people forget what you discussed the last time the feature was brought up several months ago and forward progress becomes difficult to make. I'd really like to see you guys nail down the records, tuples, and pattern matching features (perhaps another prototype?) and then move on to the next features once those have final or near-final designs. That would also allow you to consider having more incremental releases of the language. Some other thoughts on the design notes: -1. Very much opposed to this feature. Adds a lot of complexity to the language for questionable benefit. If you need to pass references around like this for performance reasons, you should probably be using C++ anyway as it gives you all the ultra-fine grained control over memory you could ever want. We shouldn't try to make C# become C++ Light. -2. Could you elaborate on where -4. This is a good idea and I hope you guys are able to work out all the corner cases. -6. As I said in the thread for this feature, I don't think its particularly valuable as analyzers can already provide warnings around this. I'm fine with the attribute approach if there's really that much demand for it, but I suspect it would be very rarely used and thus probably not worth the design effort, given other useful features on the table for C# 7.0. |
@MgSam, regarding 4. what do you find not valuable? Methods having a return type of |
@paulomorgado I think his 4 is actually 6, but became a victim of markdown auto-numbering. If you re-read Mads' post, the "Does not return" section doesn't talk about attributes and analyzers at all. |
I found it strange, but the first sentence matched it. 😄 |
@Joe4evr Crap, yes- this is the case. It renumbered my items after submitting. Fixed now. |
@MgSam The following syntax works to prevent Markdown from renumbering lists:
|
For 1), yes please! As a game developer, structs are my lifeblood. Any changes that make them easier and more efficient to use will be greatly appreciated. I often find myself using arrays over Lists of structs due to the overhead of copying them in and out of the indexer. Often I even allocate blocks of unmanaged memory via AllocHGlobal and carve out pieces of it for my value types. Being able to incorporate ref local / return support for that scenario would be great as well. (This would be even nicer to work with if some of the restrictions around pointers to generic types were relaxed, but that's a separate feature to discuss.) @MgSam, please don't tell people writing performance-sensitive code to just go back to C++. There are a million reasons why C# is a nicer language to work in, and there's no reason we can't be just as fast as C++, especially with .NET Native on the horizon. |
a) syntax-based LINQ is more like an integrated DSL, does it really matter for the rest of the language? b) Swift is not relevant (yet), it's not even mainstream in Apple development let alone other platforms. Much better example that proves the opposite would be very mainstream (granted ill-designed) JavaScript where |
I would go for |
@dsaf I agree the word "let" isn't an abbreviation of the concept it represents, but so what? This is a language symmetry argument, I'm much more concerned about how the keyword will play in practice, not whether it is nice from a theory perspective. Also, re: b), this is Apple we're talking about- once a tech falls out of favor (Objective C) there's a ticking clock until they drop support altogether. They've been pretty clear Swift will be the future for their products. |
@MgSam The design group is trying to decide on a reasonable full set of things to work on before deep diving into an arbitrarily selected feature. Once we've considered the full set of things we might do, we'll narrow it down and then begin the design work in more detail. That design work might cause us to reconsider the set of features as a whole, and that's fine. |
Another vote for |
I think Now if only |
I am not native English and let on a parameters does not sound dorky to me 2015-04-10 23:34 GMT+02:00 jnm2 [email protected]:
|
IIRC public int Divide(readonly int dividend, readonly int divisor) {
let quotient = dividend / divisor; // same as readonly int quotient
return quotient;
} |
I usually type only "v" and use IntelliSense to get |
As already mentioned in other discussions, |
Regarding Item 2, I vote for |
Hi @MadsTorgersen, as you mentioned it would be great to hear from the community about use cases for ref returns, here we go :) Background Unity (a game engine https://unity3d.com/unity) uses C# for scripting but has the majority of the engine code in C++. Our customers write almost all game code in C#. C# in games usually differs in two ways from normal C# usage:
Problem struct Particle
{
Vector3 position;
float lifetime;
Color32 color;
...
}
// Allocation is controlled by user, the particle system API does not allocate
// So allocations can be done once at startup and never during gameplay.
Particle[] particleArray = new Particle[1000];
...
void Update ()
{
int nbParticles = particleSystem.GetParticles(particleArray);
for (int i=0;i<nbParticles;i++)
particleArray[i].position.y += 5.0F * deltaTime;
particleSystem.SetParticles(particleArray, nbParticles);
} The issue with this approach is that we copy the particle buffer (It's easy to imagine 10.000 particles -> 700kb of data) into managed land. Then we modify the data in the array. Then we modify the particles, then we copy them back into the array. It is easy to see how this approach can easily be 3x slower than it has to be for simple computations, due to the extra copies. We have experimented with our own Buffer struct. Which gives us direct access to unmanaged memory allocated by the engine. struct Buffer<T>
{
IntPtr buffer;
int stride;
int length;
unsafe public T this[int x]
{
get ...
set ...
}
public int Length
{
get ...
}
// NOTE: the Buffer struct also has some debug only mode
// which allows it to track when a buffer has been deallocated on the C++ side
// and throw exceptions.
// So the Buffer struct is in fact safe, but that part is not relevant for discussion here.
// Also note that the buffer class itself throws exceptions when using it with any non-value types.
}
struct Particle
{
Vector3 position;
float lifetime;
Color32 color;
...
}
// The following code, directly writes into the particle buffer on the C++ side. No extra copies.
// Performance issues are solved...
// Additionally no managed array has to be allocated, which reduces overall memory consumption dramatically,
// as well as generally keeping the GC heap tighter.
// Also the code is much simpler.
// This is what we want to write!
void Update ()
{
Buffer<Particle> buffer = particleSystem.GetParticleBuffer(particleArray);
for (int i=0;i<buffer.Length;i++)
buffer[i].position.y += 5.0F * deltaTime; // But this line gives us a compile error in C#
}
// This is what we have to write today.
// And it is bad for many reasons, see below.
void Update ()
{
Buffer<Particle> buffer = particleSystem.GetParticleBuffer(particleArray);
for (int i=0;i<buffer.Length;i++)
{
Particle particle = buffer[i];
particle.position.y += 5.0F * deltaTime;
buffer[i] = particle;
}
} The first example doesn't compile today. This not being possible is bad for two reasons
The general issue is that today, structs in C# are severely limited. Structs are also the key to high performance computation in C#, because it is the only way in which you can accurately control memory layout, which is naturally by far the key concern for high performance code. Solution: ref returns I hope that this post shows why we believe ref support in C# is an essential part to do high performance computation in C#. Without ref returns support, it will not be possible to achieve performance to match C++ for computation intensive code. The result is that we have to ask our customers to write their particle systems in C++, while we would love to ask them to write them in C# instead. This problem is everywhere in the domain of games. This particle system is just one example. |
Design notes have been archived at https://github.com/dotnet/roslyn/blob/future/docs/designNotes/2015-03-24%20C%23%20Design%20Meeting.md but discussion can continue here. |
C# Design Meeting Notes for Mar 24, 2015
Quote of the Day: If we have slicing we also need dicing!
Agenda
In this meeting we went through a number of the performance and reliability features we have discussed, to get a better reading on which ones have legs. They end up falling roughly into three categories:
As follows:
Some of these were discussed again (see below), some we just reiterated our position.
#1. Ref returns and locals
At the implementation level these would require a verifier relaxation, which would cause problems when down targeting in sandboxing scenarios. This may be fine.
At the language level, ref returns would have to be allowed on properties and indexers only if they do not have a setter. Setters and ref would be two alternative ways of allowing assignment through properties and indexers. For databinding scenarios we would need to check whether reflection would facilitate such assignment through a ref.
A danger of ref returns in public APIs: say you return a ref into the underlying array of e.g. a list, and the list is grown by switching out the underlying array. Now someone can get a ref, modify the collection, follow the ref and get to the wrong place. So maybe ref returns are not a good thing on public API boundary.
There's complexity around "safe to return": You should only return refs that you received as parameters, or got from the heap. This leads to complexity around allowing reassignment of ref locals: how do you track whether the ref they are pointing to is "safe to return" or not? We'd have to either
There's complexity in how refs relate to readonly. You either can't take a ref to a readonly, or you need to be able to express a readonly ref through which assignments is illegal. The latter would need explicit representation in metadata, and ideally the verifier would enforce the difference.
This can't very well be a C# only feature, at least if it shows up in public APIs. VB and F# would need to at least know about it.
This feature would be a decent performance win for structs, but there aren't a lot of structs in the .NET ecosystem today. This is a chicken-and-egg thing: because structs need to be copied, they are often too expensive to use. So this feature could lower the cost of using structs, making them more attractive for their other benefits.
Even so, we are still a bit concerned that the scenario is somewhat narrow for the complexity the feature adds. The proof will have to be in the use cases, and we're not entirely convinced about those. It would be wonderful to hear more from the community. This is also a great candidate for a prototype implementation, to allow folks to experiment with usability and performance.
#2. Readonly locals and parameters
At the core this is a nice and useful feature. The only beef we have with it is that you sort of want to use
readonly
to keep your code safe, and you sort of don't because you're cluttering your code. Thereadonly
keyword simply feels a bit too long, and it would be nice to have abbreviations at least in some places.For instance
readonly var
could be abbreviated toval
orlet
. Probablyval
reads better thanlet
in many places, e.g. declaration expressions. We could also allowval
as an abbreviation forreadonly
even in non-var situations.In Swift they use
let
but it reads strange in some contexts. In Swift it's optional in parameter positions, which helps, but we couldn't have that for back compat reasons.This is promising and we want to keep looking at it.
#4. Does Not Return
It would be useful to be able to indicate that a method will never return successfully. It can throw or loop.
The proposal is to do it as an attribute, but there would be more value if it was part of the type system. Essentially it replaces the return type, since nothing of that type is ever returned. We could call it
never
. Thenever
type converts to pretty much anything.This would allow us to add throw expressions in the language - their type would be
never
.Having it in the type system allows e.g. returning
Task<never>
, so that you can indicate an async method that will only ever produce an exception, if anything.Because of the Task example you do want to allow
never
in generics, but that means you could have generic types that unwittingly operate on never values, which is deeply strange. This needs to be thought about more.If through nasty tricks you get to a point in the code that according to never types should not be reachable, the code should probably throw.
A common usage would be helper methods to throw exceptions. But throw as an expression is the most useful thing out of this.
#6. Attributes on lambdas
Why? Guiding an analyzer, e.g. to prevent variable capture. Syntactically it might collide with XML literals in VB.
We could probably hack it in. The attribute would be emitted onto the generated method.
The text was updated successfully, but these errors were encountered: