Freigeben über


What's The Difference, Part Two: Scope vs Declaration Space vs Lifetime

"Scope" has got to be one of the most confusing words in all of programming language design. People seem to use it casually to mean whatever is convenient at the time; I most often see it confused with lifetime and declaration space. As in "the memory will be released when the variable goes out of scope".

In an informal setting, of course it is perfectly acceptable to use "scope" to mean whatever you want, so long as the meaning is clearly communicated to the audience. In a more formal setting, like a book or a language specification, it's probably better to be precise.

The difference between scope and declaration space in C# is subtle.

The scope of a named entity is the region of program text in which it is legal to refer to that entity by its unqualified name.

There are some subtleties here. The implication does not "go the other way" -- it is not the case that if you can legally use the unqualified name of an entity, that the name refers to that entity. Scopes are allowed to overlap. For example, if you have:

class C
{
int x;
void M()
{
int x;
}
}

then the field is in scope throughout the entire body text of C, including the entirity of M. Local variable x is in scope throughout the body of M, so the scopes overlap. When you say "x", whether the field or the local is chosen depends on where you say it.

A declaration space, by contrast, is a region of program text in which no two entities are allowed to have the same name. For example, in the region of text which is body of C excluding the body of M, you're not allowed to have anything else named x. Once you've got a field called x, you cannot have another field, property, nested type, or event called x.

Thanks to overloading, methods are a bit of an oddity here. One way to characterize declaration spaces in the context of methods would be to say that "the set of all overloaded methods in a class that have the same name" constitutes an "entity". Another way to characterize methods would be to tweak the definition of declaration space to say that no two things are allowed to have the same name except for a set of methods that all differ in signature.

In short, scope answers the question "where can I use this name?" and declaration space answers the question "where is this name unique?"

Lifetime and scope are often confused because of the strong connection between the lifetime and scope of a local variable. The most succinct way to put it is that the contents of a local variable are guaranteed to be alive at least as long as the current "point of execution" is inside the scope of the local variable. "At least as long" of course implies "or longer"; capturing a local variable, for example, extends its lifetime.

Comments

  • Anonymous
    August 03, 2009
    The comment has been removed

  • Anonymous
    August 03, 2009
    The comment has been removed

  • Anonymous
    August 03, 2009
    The comment has been removed

  • Anonymous
    August 04, 2009
    The comment has been removed

  • Anonymous
    August 04, 2009
    "no two things are allowed to have the same name except for a set of methods that all differ in signature" Alternatively, you could consider the method "name" to be a combination of the name and signature. For example, given: void M() void M(int) void M(string) void M(int, string) you could assume that the method names look something like: M M@Int32 M@String M@Int32@String As I recall, the C++ compiler does something like this for overloading exported functions.

  • Anonymous
    August 04, 2009
    > force the application to clean up resources as soon as possible instead of at the end of each method Neither scope nor declaration space of your locals have any effect on when GC will clean up resources. If you look at the IL generated from your code, you'll see that all local variables in a method are actually declared as "method scope" in IL. It doesn't really matter in the end, because the runtime is pretty smart at figuring out when a given local variable won't ever be used again, and thus should no longer be considered a root for GC purposes.

  • Anonymous
    August 04, 2009
    The comment has been removed

  • Anonymous
    August 05, 2009
    The comment has been removed

  • Anonymous
    August 05, 2009
    "Alternatively, you could consider the method "name" to be a combination of the name and signature. For example, given:" It's rather complex because it, in your example includes only the name and return type of the signature for the purposes of treating them as one entity with multiple parts. Anything with a different return type in that situation is considered a clash within the declarative space. However when you do it with methods in the base class as well public class Bottom { public int Blah() {return 0;} } public class Bar : Bottom {    public double Blah() {return 0.0;} } Then this is considered acceptable (albeit with a compiler warning without new) as you are allowed to shadow, thus cover up one declarative space with another. Of course the moment you make Bottom's Blah private this issue goes away so the maximum access level in the base class(es) alters the declarative space for sub classes but has no effect on the space within the class. but wait. it gets worse :) public interface IFoo { int Blah(); } public class Bottom : IFoo {     int IFoo.Blah() {return 0;} } public class Bar : Bottom {    public double Blah() {return 0.0;} } suddenly the compiler warning goes away because we have pushed the declarative space into the interface and, despite Bar being usable as an IFoo and truly implementing it, it is only accessible from variables where you stop being able to treat it as a Bar, hence the spaces cannot clash. Good that the compiler is clever about this but you begin to see the complexity of it. I'd hate to have to implement a compiler, though it's probably a source of combined maddening and marvellous all at once :)

  • Anonymous
    May 27, 2010
    > The scope of a named entity is the region of program text > in which it is legal to refer to that entity by its unqualified name. > ... > [x] is in scope throughout the entire body text of C, including the > entirity of M. So just to clarify, within the method M() of your example, saying "this.x" does not count as qualifying the name?  What does unqualified mean, exactly? Hobbes

  • Anonymous
    June 20, 2010
    i think many people might arrive at this website because they are hitting the 'variable name is already defined' and such like errors. I would point out to those people wishing to re-use a variable name that it is perfectly ok as long as the scopes do not form a parent-child relation ship. In other words it is fine to have foreach and for loops use the same variable names as long as the same name does not also exist in the enclosing scope For example foreach (string s in list)  Console.WriteLine(s) foreach (string s in anotherlist) Console.WriteLine(s) is fine as long as s in not also declared in the enclosing scope of the loops

  • Anonymous
    November 08, 2010
    The comment has been removed

  • Anonymous
    November 08, 2010
    Or in short: the rules for determining scope don't depend on if other entities have the same name.  See here: msdn.microsoft.com/.../aa691132(v=VS.71).aspx When there are multiple entities with the same name and overlapping scope, you have name hiding, as described in the subsequent sections of that specification.