为什么 C # 不像 C 那样支持本地静态变量?

为什么 C # 没有像 C 那样的本地静态变量? 我错过了! !

41295 次浏览

C# is a component-oriented language and doesn't have the concept of variables outside the scope of a class or local method. Variables within a method cannot be declared static either, as you may be accustomed to doing in C. However, you can always use a class static variable as a substitute.

As a general practice, there are usually ways to solve programming problems in C# without resorting to using method-level statics. State is generally something you should design into classes and types, not methods.

State is generally part of an object or part of a type, not part of a method. (The exception being captured variables, of course.)

If you want the equivalent of a local static variable, either create an instance variable or a static variable - and consider whether the method itself should actually be part of a different type with that state.

I'm not nearly as familiar with C as I am C#, but I believe you can accomplish everything you could with a local static, by using a class level static that is only used for one method. Obviously, this comes with some syntactic change, but I believe you can get whatever functionality you need.

Additionally, Eric Lippert answers questions like this on his blog a lot. Generally answered in this way: "I am asked "why doesn't C# implement feature X?" all the time. The answer is always the same: because no one ever designed, specified, implemented, tested, documented and shipped that feature." Essentially his answers generally boil down to, it costs money to add any feature, and therefore, many potential features are not implemented because they have not come out on the positive side of the cost benefit analysis.

I think the idea of local statics is just as easily solved by creating public static fields to the class. Very little logical change don't you think?

If you think it would be a big logical change, I'd be interested to hear how.

class MyClass
{
public static float MaxDepthInches = 3;


private void PickNose()
{
if (CurrentFingerDepth < MyClass.MaxDepthInches)
{
CurrentFingerDepth++;
}
}
}

Because they screwed up, and left out a useful feature to suit themselves.

All the arguments about how you should code, and what's smart, and you should reconsider your way of life, are pompous defensive excuses.

Sure, C# is pure, and whatchamacallit-oriented. That's why they auto-generate persistent locals for lambda functions. It's all so complicated. I feel so dumb.

Loop scope static is useful and important in many cases.

Short, real answer, is you have to move local statics into class scope and live with class namespace pollution in C#. Take your complaint to city hall.

The MSDN blog entry from 2004: Why doesn't C# support static method variables? deals with the exact question asked in the original post:

There are two reasons C# doesn't have this feature.

First, it is possible to get nearly the same effect by having a class-level static, and adding method statics would require increased complexity.

Second, method level statics are somewhat notorious for causing problems when code is called repeatedly or from multiple threads, and since the definitions are in the methods, it's harder to find the definitions.

[author: Eric Gunnerson]

(Same blog entry in the Microsoft's own archive. The Archive.org preserved the comments. Microsoft's archive didn't.)

Logically, yes. It would be the same as a class-level static member that was only used in that one method. However, a method-level static member would be more encapsulated. If the data stored in a member is only meant to be used by a single method, it should only be accessible by that single method.

However, you CAN achieve almost exactly the same effect in C# by creating a nested class.

Because static local variables are tied to the method, and the method is shared amongst all instances.

I've had to correct myself and other programmers who expect it to be unique per class instance using the method.

However, if you make it a static class, or static instance of a class, it's syntactically clear whether there's an instance per container-class, or one instance at all.

If you don't use these, it becomes easier to refactor later as well.

You can simulate it using a delegate... Here is my sample code:

public Func<int> Increment()
{
int num = 0;
return new Func<int>(() =>
{
return num++;
});
}

You can call it like this:

 Func<int> inc = Increment();
inc();

If you can imagine some sort of Lippert/Farnsworth hybrid entity announcing GOOD NEWS EVERYONE!, C# 6.0 allows the using static statement. This effectively allows you to import static class methods (and, it seems, properties and members as well) into the global scope.

In short, you can do something like this:

using NUnit.Framework;
using static Fizz.Buzz;


class Program
{
[Test]
public void Main()
{
Method();
int z = Z;
object y = Y;
Y = new object();
}
}




namespace Fizz
{
class Buzz
{
public static void Method()
{
}


public static int Z;


public static object Y { get; set; }
}
}

While this is only available in C# 6.0, from what I understand the generated assemblies should be compatible with previous .NET platforms (correct me if I'm wrong).

So you want to use a static local variable in your method? Congratulations! You made another step towards becoming a real programmer.

Don't listen to all the people telling you that static locals are not "clean", that they impede "readability" and could lead to subtle and hard-to-find "bugs". Nonsense! They just say that because they are wannabe programmers! Lots of them are probably even toying around with an esoteric functional programming language during their free-time. Can you believe it? What a bunch of hipsters!

Real programmers embrace a paradigm I like to call SDD - Side effect Driven Design. Here are some of it's most important laws:

Don't be predictable! Never return the same thing from a method twice - even if it's being called with the exact same arguments!

Screw purity - let's get dirty! State, by nature, craves changing, because it is an insatiable monoid in the category of polyamorous endofunctors, i.e. it likes to be touched by as many collaborators as possible. Never miss out on an opportunity to do it the favor!

Among the tools used to code in a side effect driven manner are, of course, static local variables. However, as you noticed, C# does not support them. Why? Because over the last two decades Microsoft has been infiltrated by so called Clean Coders that favor maintainability over flexibility and control. Can you even remember the last time you have seen our beloved blue screen? Now guess whose fault is that!

But fear not! Real developers don't have to suffer from those poor design decisions. As has been mentioned before it is possible to have local variables that are kind of static with the help of lambdas.

However, the provided solution wasn't quite satisfactory. Using the previous answer our almost-SDD-compliant code would look something like this:

var inc = Increment();
var zero = inc();
var one = inc();

or

var zero = Increment()();

But that's just silly. Even a wannabe developer can see that Increment() is not a normal method and will get suspicious. A real programmer, on the other hand, can make it even more SDD-like. He or she knows that we can make a property or field look like a method by giving it the type Func<T>! We just have to initialize it by executing a lambda that in turn initializes the counter and returns another lambda incrementing the captured counter!

Here it is in proper SDD code:

public Func<int> Increment = new Func<Func<int>>(() =>
{
var num = 0;
return () => num++;
}).Invoke();

(You think the above kinda looks like an IIFE? Yes, you are right and you should be ashamed of yourself.)

Now every time you call Increment() it will return something different:

var zero = Increment();
var one = Increment();

Of course you also can make it so that the counter survives the lifetime of your instance.

That'll show them wannabe programmers!

You can use nested-class as a workaround for this. Since C# is limiting the scope of static variables to classes, you can use nested-class as a scope.

For example:

public class Foo {
public int Increment() {
return IncrementInternal.Increment();
}
    

private static class IncrementInternal {
private static int counter = 0;
public static int Increment() {
return counter++;
}
}
}

Here Foo supports Increment method, but its support it by the private nested class IncrementInternal which contains the static variable as a member. And of course, counter is not visible in the context (other methods) of Foo.

BTW, if you want to access to Foo context (other members and methods) inside IncrementInternal.Increment, you can pass this as a parameter to IncrementInternal.Increment when you call it from Foo.

To keep the scope as small as possible, my suggestion is to create a nested class per each such method. And because it is probably not so common, the number of nested classes will stay small enough to maintains it.

I think it is cleaner than anonymous functions or IIFE.

You can see a live demo here.

I don't see much added benefit to local statics, if you are keeping your classes single purpose and small, there is little problem with global static pollution as the naysayers like to complain about. But here is just one other alternative.

 using System;
using System.Collections;


public class Program
{
delegate bool DoWork();


public static void Main()
{
DoWork work = Foo().GetEnumerator().MoveNext;


work();
work();
work();
}


public static IEnumerable Foo()
{
int static_x = 10;
/*
do some other static stuff....
*/


main:


//repetative housework
Console.WriteLine(static_x);
static_x++;




yield return true;
goto main;
}




}