What is meant by the "scope" of a variable? think of the scope of a variable as "The scope of a particular variable is the range within a program's source code in which that variable is recognized by the compiler".
That definition matches the definition of scope in the C# specification; it says:
The scope of a name is the region of program text within which it is possible to refer to the entity declared by the name without qualification of the name
Notice that the C# specification calls out that it is the name which has a scope. Some variables do not have names, and therefore have no scope. And some things have names but are not variables; class names, namespace names, function names, and so on, all have a scope.
The scope of a local variable is frequently confused with its lifetime. The lifetime of a local variable is sometimes related to its scope; when control leaves the scope of a local (either through normal means or via an exception) then the local can be destroyed without anyone noticing. However, the runtime is permitted to make the lifetime of a local variable shorter if it can do so without anyone noticing. The runtime is also permitted to make a local variable live longer than its scope. And there are some situations in which a local variable is required to live beyond when control leaves its scope. (For instance, when the local is a closed-over outer variable of a lambda.)
Therefore it is unwise to use "scope" as a synonym for "lifetime". They are often related but can be quite different. Use "scope" to mean "the region of text in which a name is valid", not "the period of time in which a variable is alive". Scope is fundamentally a compile-time concept.
What exactly is the difference between the scope of variables in C# and (C99, C++, Java)
I am not sufficiently familiar with the exact scoping rules of C, C++ or Java to give a short list of the differences. I can however point out some oddities of C#.
The first is that in C#, a local variable is in scope throughout its block.
class C
{
int x;
void M()
{
int y = x;
int x = y;
}
}
In C++, the first "x" means "this.x" because the local variable x is not in scope until its declaration. In C#, the local variable x is in scope throughout the block. Using a local that is in scope before its declaration is an error in C#.
The second is that in C#, a simple name must mean the same thing throughout the scope in which it is first used:
class C
{
int x;
void M()
{ // M starts
int y = x;
if (y > 100)
{
int x = y;
}
}
}
This is illegal because inside the block labelled "M starts" the simple name "x" is used to mean two different things. Now the first "x" means "this.x" because the local is not in scope. But it is illegal to use the same name to mean two different things, because that is a source of bugs.
If this subject interests you, I have written a number of articles on it. See
http://blogs.msdn.com/b/ericlippert/archive/tags/scope/
http://blogs.msdn.com/b/ericlippert/archive/tags/declaration+spaces/
UPDATE: The rule that a simple name must have a unique meaning throughout the block which encloses it has been relaxed in C# 6. The language design team decided that the cost of user confusion was too high for the benefit in catching bugs.
Limit the use of scopes or more concretely Use scopes for wiring only.
Used properly, scopes can reduce a lot of factory boiler plate. I use scopes to wire together sub-processes that may need access to its name and arguments. This is similar to the RequestScope
s provided by Guice and Spring.
But scopes are effectively a thread-local map of string to object. This is practically a Global Variable Depot. This is why I limit my scope usage.
This leads to the corollary:
Hide the scopes or more generally Hide your DI Framework
Because scopes (and DI frameworks) are essentially Global Variable Depots, I prefer to encapsulate the DIF such that the only thing that knows there is a DIF is the main method.
To do this, I define my own scope interface and my own ProcessFactory that I define within a Guice module.
Because of this, my process manager is free of references to Guice. In fact, it is little more than this method:
void run(final ProcessContext context) {
try {
this.scope.enter(context.processArgs());
this.factory.create(args.processName());
} finally {
this.scope.exit();
}
}
Here's the complete Guice module that binds and hides my use of a Guice. It implements my own ProcessScope and ProcessFactory interfaces. It binds @ProcessParameters so they can be injected and a few convienience objects as well (@ProcessName String and ProcessConfig).
public class ProcessRunnerModule extends AbstractModule {
private static final String PROCESS_RUN_SCOPE = "ProcessRunScope";
/**
* Objects of type Map<String, Object> parameterized by @ProcessParameters
* are injected in the ProcessRunScope
*/
static final Key<Map<String, Object>> PROCESS_PARAMETERS_KEY;
static {
final TypeLiteral<Map<String, Object>> MAP_TYPE = new TypeLiteral<Map<String, Object>>() {
};
PROCESS_PARAMETERS_KEY = Key.get(MAP_TYPE, ProcessParameters.class);
}
/**
* Wraps Guice scope to ProcessScope. Injects the @ProcessParameters into guice
*/
private static class GuiceScopeAdapter implements ProcessScope {
private final SimpleScope scope;
@Inject @SuppressWarnings("unused")
public GuiceScopeAdapter(@Named(PROCESS_RUN_SCOPE) SimpleScope scope) {
this.scope = scope;
}
@Override
public void enterScope(Map<String, Object> parameters) {
scope.enter();
scope.seed(PROCESS_PARAMETERS_KEY, parameters);
}
@Override
public void exitScope() {
scope.exit();
}
}
/**
* Processes are run and bound in @ProcessRunScope.
*/
protected void configure() {
final SimpleScope processRunScope = new SimpleScope();
bindScope(ProcessRunScope.class, processRunScope);
bind(SimpleScope.class)
.annotatedWith(Names.named(PROCESS_RUN_SCOPE))
.to(processRunScope);
}
/**
* This wraps Processes bound via MapBinder to a ProcessFactory
*/
@Provides @Singleton
ProcessFactory createProcessFactory(final Map<String, Provider<Process>> processFactories) {
log.info("Instantiating process factory", "known-processes", processFactories.keySet());
return new ProcessFactory() {
@Override
public Process create(final String name) {
return processFactories.get(name).get();
}
};
}
/**
* ProcessRunner does not know about Guice
*/
@Provides @Singleton
ProcessRunner createProcessRunner(
final ProcessScope processScope,
final ProcessFactory processFactory) {
return new ProcessRunner(processScope, processFactory);
}
/**
* Convienience: bind a @ProcessName extracted from the @ProcessParameters
*/
@Provides @ProcessName @ProcessRunScope
String bindProcessName(final @ProcessParameters Map<String, Object> params) {
return params.get(ProcessRunner.PROCESS_NAME_KEY).toString();
}
/**
* Convienience: bind a ProcessConfig wrapping the @ProcessParameters
*/
@Provides @ProcessRunScope
ProcessConfig createParamHelper(final @ProcessParameters Map<String, Object> params) {
return new ProcessConfig(params);
}
}
Best Answer
You'll need some kind of mental bookkeeping tricks (naming conventions, etc) in order to keep it straight. Also, document, document, document. Since all variables are globals, have a single document with all of them listed, if you can.
Try to have a small number of variables that you always use for temporaries, and remember that THEY ARE TEMPORARY. By constantly re-using the same ones, you'll get into the habit of keeping track of where they are valid or not.
Also, you want to look at the documentation and make sure you know how long variable names can be, and how many characters are actually unique. I know NOTHING about PowerOn, but if it's archaic enough to have only global scope, then it's possible that it's got a limited uniqueness length on identifiers.
I've seen things before with long identifiers, but whose identifiers were only unique in the first 8 characters. So you could have RonnyRayGun and RonnyRayBlaster and they are actually the SAME variable. In such cases I recommend keeping variable names under the 'unique' limit so that you're less likely to accidentally collide.