To illustrate my confusion, see the following example:
int a = 0;
Action act = () => ++a;
act();
Console.WriteLine(a);
I have difficulty figuring out how modification to the captured variable within the lambda could possibly affect the local variable a. First of all, the implicitly generated lambda function object could not store a reference to the local variable a. Otherwise, if act is returned and invoked later, the referenced local variable a would already be vanished. A solution to this problem would be copy-by-value, either by copying the int value directly or through boxing, so that the function object would have its own copy the local variable a. But this doesn't explain the example just given. So, what is the underlying mechanism? Would it be that the seemingly local variable a is actually no longer a local variable, but translated by the compiler to be a reference to an int field within the generated lambda function object?
The point here is closure. After compilation a is not a local variable anymore - it's a field of auto-generated class, both in function scope and in lambda.
Related
I have the following code to modify a property of the ParticleSystem in Unity
waterCone.main.loop = true;
However, this is giving me the following error
Property 'main' access returns temporary value. Cannot modify struct member when accessed struct is not classified as a variable
Unity's own docs seem to show this code being used as follows
var waterConeMain = waterCone.main;
waterConeMain.loop = true;
With no errors at all.
Why does this fix the problem, as far as my understanding of c# goes, this should be the exact same.
In fact, as far as I can tell neither should work.
I tried to compare the two in Sharplab but the first would not even compile.
What's going on here? And why is this variable necessary?
(edit)
From looking into the source code, it looks like ParticleSystem.main is doing this
MainModule main => new MainModule(this);
And Main is a struct, which has loop as a property like
get => ParticleSystem.MainModule.get_loop_Injected(ref this);
set => ParticleSystem.MainModule.set_loop_Injected(ref this, value);
But really this seems to leave me with more questions than answers, why is this a compilation error when I don't use a temporary variable?
The return type of ParticleSystem.main is a struct type, you cannot directly modify its value. The main reason why you can't do this is that the returned struct value is just a copy, so it's usually meaningless to modify the value at this time and it will cause some imperceptible errors. Quote the explanation from official document
This error occurs because value types are copied on assignment. When you retrieve a value type from a property or indexer, you are getting a copy of the object, not a reference to the object itself. The copy that is returned is not stored by the property or indexer because they are actually methods, not storage locations (variables). You must store the copy into a variable that you declare before you can modify it.
Based on this recent question, I don't understand the answer provided. Seems like you should be able to do something like this, since their scopes do not overlap
static void Main()
{
{
int i;
}
int i;
}
This code fails to compile with the following error:
A local variable named 'i' cannot be declared in this scope because it would give a different meaning to 'i', which is already used in a 'child' scope to denote something else
I don't think any of the answers so far have quite got the crucial line from the spec.
From section 8.5.1:
The scope of a local variable declared in a local-variable-declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local-variable-declarator of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.
(Emphasis mine.)
In other words, the scope for the "later" variable includes the part of the block before the declaration - i.e. it includes the "inner" block containing the "earlier" variable.
You can't refer to the later variable in a place earlier than its declaration - but it's still in scope.
"The scope of local or constant variable extends to the end of the current block. You cannot declare another local variable with the same name in the current block or in any nested blocks." C# 3.0 in a Nutshell, http://www.amazon.com/3-0-Nutshell-Desktop-Reference-OReilly/dp/0596527578/
"The local variable declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block." Variable Scopes, MSDN, http://msdn.microsoft.com/en-us/library/aa691107%28v=vs.71%29.aspx
On a side note, this is quite the opposite that of JavaScript and F# scoping rules.
From the C# language spec:
The local variable declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block.
Essentially, it's not allowed because, in C#, their scopes actually do overlap.
edit: Just to clarify, C#'s scope is resolved at the block level, not line-by-line. So while it's true that you cannot refer to a variable in code that comes before its declaration, it's also true that its scope extends all the way back to the beginning of the block.
This has been a rule in C# from the first version.
Allowing overlapping scopes would only lead to confusion (of the programmers, not the compiler).
So it has been forbidden on purpose.
For C#, ISO 23270 (Information technology — Programming
languages — C#), §10.3 (Declarations) says:
Each block, switch-block, for-statement, foreach-statement, or
using-statement creates a declaration space for local variables and
local constants called the local variable declaration space. Names are
introduced into this declaration space through local-variable-declarations
and local-constant declarations.
If a block is the body of an instance
constructor, method, or operator declaration, or a get or set accessor for
an indexer declaration, the parameters declared in such a declaration are
members of the block’s local variable declaration space.
If a block is the
body of a generic method, the type parameters declared in such a declaration
are members of the block’s local variable declaration space.
It is an error
for two members of a local variable declaration space to have the same name.
It is an error for a local variable declaration space and a nested local
variable declaration space to contain elements with the same name.
[Note: Thus, within a nested block it is not possible to declare a local
variable or constant with the same name as a local variable or constant
in an enclosing block. It is possible for two nested blocks to contain
elements with the same name as long as neither block contains the other.
end note]
So
public void foobar()
{
if ( foo() )
{
int i = 0 ;
...
}
if ( bar() )
{
int i = 0 ;
...
}
return ;
}
is legal, but
public void foobar()
{
int i = 0 ;
if ( foo() )
{
int i = 0 ;
...
}
...
return ;
}
is not legal. Personally, I find the restriction rather annoying. I can see issuing a compiler warning about scope overlap, but a compilation error? Too much belt-and-suspenders, IMHO. I could see the virtue of a compiler option and/or pragma , though ( perhaps -pedantic/-practical, #pragma pedantic vs #pragma practical, B^)).
It's not a question of overlapping scopes. In C# a simple name cannot mean more than one thing within a block where it's declared. In your example, the name i means two different things within the same outer block.
In other words, you should be able to move a variable declaration around to any place within the block where it was declared without causing scopes to overlap. Since changing your example to:
static void Main()
{
int i;
{
int i;
}
}
would cause the scopes of the different i variables to overlap, your example is illegal.
I just compiled this in GCC both as C and as C++. I received no error message so it appears to be valid syntax.
Your question is tagged as .net and as c. Should this be tagged as c#? That language might have different rules than C.
In C you need to put all variable declaration at the very beginning of a block. They need to come all directly after the opening { before any other statements in this block.
So what you can do to make it compile is this:
static void Main()
{
{
int i;
}
{
int i;
}
}
Here's your answer from MSDN .NET Documentation:
...The local variable declaration space of a block includes any nested blocks. Thus, within a nested block it is not possible to declare a local variable with the same name as a local variable in an enclosing block.
Why does taking the address of a variable eliminate the "Use of unassigned local variable" error?
(Why can we take the address without initialization in the first place?)
static unsafe void Main()
{
int x;
int* p = &x; //No error?!
x += 2; //No error?!
}
C# Language spec, section 18.5.4:
The & operator does not require its argument to be definitely assigned, but following an & operation, the variable to which the operator is applied is considered definitely assigned in the execution path in which the operation occurs. It is the responsibility of the programmer to ensure that correct initialization of the variable actually does take place in this situation.
...
The rules of definite assignment for the & operator exist such that redundant initialization of local variables can be avoided. For example, many external APIs take a pointer to a structure which is filled in by the API. Calls to such APIs typically pass the address of a local struct variable, and without the rule, redundant initialization of the struct variable would be required.
I think because, once you've taken a pointer to the variable, there's no way for the compiler to analyze whether a value is assigned via that pointer, so it's excluded from the definite assignment analysis.
I have the following code:
public void SetMove(Position3D pos, float time, float linearity, bool relative)
{
ExecuteOnActiveClients(delegate(NeuroClient client)
{
client.Engine.GetProcAnimation(name).SetMove(pos, time, linearity, relative);
}, true, true);
}
Where ExecuteOnActiveClients pushes the delegate in a queue, consumed asynchronously, and has the following signature:
void ExecuteOnActiveClients(ClientDelegate action, Boolean parallel, Boolean wait, Boolean lockClient)
I have a lot of functions which look like this one, and which may be called concurrently.
I have noticed that I must store the value of name (which is a private field of the class) in a variable in the function before I execute ExecuteOnActiveClients to have this code work well, because if I don't, the delegate uses the last value of name, and not the value the field had when the function was called.
I guess this is a problem of scope, because the parameters of the function (pos, time, linearity and relative) are correct.
Is there a way to force the delegate to use the value of name when it was created, and not the value of name when it is executed?
I'd like to avoid to store the value in each of the lot of functions which use ExecuteOnActiveClients if it possible.
Thanks in advance
Mike
Currently the delegate isn't storing the value of name at all. It's capturing this, and then using it to resolve this.name whenever you refer to it.
This is the way both anonymous methods and lambda expressions work, and there's nothing you can do to change their behaviour: creating a local variable is the workaround. (That will still be capturing that variable rather than its current value, but you can presumably make sure you don't change the local variable's value afterwards.)
For more information, and another trap you can easily fall into, I urge you to read Eric Lippert's blog posts on "Closing over the loop variable considered harmful" (part 1, part 2).
Just before creating your delegate, create a temporary local variable containing the value of name, and use that in your delegate.
My variable is a string at the package level and have added it as readwritevariable
example:
public void Main()
{
string sServer = "localhost";
Dts.Variables["User::sourceServer"].Value = sServer;
}
will result in
Error: The type of the value being assigned to variable differs from the current variable type. Variables may not change type during execution. Variable types are strict, except for variables of type Object.
If you've verified that the variable is indeed a string at the package level, ensure that there are no other variables with a narrower scope that might have a different type. If you have a variable scoped to the script task, or a container that the script task is in, SSIS will use that variable instead of the package-level variable.
You can check this in the designer by clicking on the script task, and ensuring that a new variable doesn't appear in the variable toolbar.