为什么C#不像C那样支持局部静态变量?[关闭]


69

为什么C#没有像C这样的局部静态变量?我很想念!!


4
您必须要问设计师。
约翰·桑德斯

9
Trivia:VB.NET使用Static关键字支持此功能。
vcsjones 2012年

3
这不只是琐事-似乎语言设计师得出了相反的结论。知道为什么会很有趣。
2012年

2
我知道C#静态意味着什么...我的问题是在C意义上使用静态
JoelFan 2014年

2
@Atomosk每次函数加载时都希望值相同的任何地方,而不必将其公开给完整类。
AustinWBryan '18

Answers:


-7

您可以使用委托对其进行仿真...这是我的示例代码:

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

您可以这样称呼它:

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

1
一年半以前,使用相同的通用方法发布了另一个答案。
Servy 2014年

8
非常
讲究

6
这什么也解决不了。每次调用该方法时,都会有一个新Increment函数num = 0。如果将Increment方法更改为静态,为什么不使用private static num = 0;?你一无所获。
罗布

1
接受的答案得分为-3。什么
cp.engr

1
除了这个答案应该是静态的以外,这个答案实际上没有任何意义。为什么不添加您所说的私有静态变量,但这实际上是手头的问题,为什么没有局部静态变量。显然答案就是您所建议的,为什么不使用类local static呢?现在我们遇到了一个真正的问题,那就是静态类vs本地静态类,这是更好的,本地静态所需的上下文并不是一个超级简单的上下文。
RJ汤普森

143

因为他们搞砸了,却没有适合自己的有用功能。

关于应该如何编码,什么是聪明的东西以及应该重新考虑生活方式的所有争论都是夸张的防御辩解。

当然,C#是纯净的,并且面向whatchamacallit。这就是为什么他们会自动为lambda函数生成持久性本地变量的原因。太复杂了。我好笨

循环作用域静态在许多情况下都是有用且重要的。

简短而真实的答案是,您必须将局部静态变量移入类范围并在C#中承受类名称空间污染。带您去市政厅投诉。


59
+1同意,这里的大多数答案都以“您可以通过...等等实现”结束,但这还不够。我可以通过多种方式实现所需的行为,但这不是重点。静态局部变量与静态类变量完全相同,但是具有较少的可见性,这是​​所需的行为。
Adam Naylor

4
实际上,在.NET中是可能的,但C#却没有。例如,VB.NET支持局部静态变量。
Stefan Steiger 2012年

8
实际上,VB.NET只是在后台创建静态的类级变量。CLR不支持静态方法变量。
JDB仍记得Monica

10
每当您说“您可以做的任何事...您也可以做的”时,都必须以为什么不编写图灵机为结尾?能够缩小变量的范围及其寿命,这是非常有用的,并且静态实例字段不会阻止其他开发人员在不应该使用的时候进行篡改-否则,为什么要麻烦public / private / etc。
NetMage 2014年

3
我希望我能给这个答案几千张赞成票。对我而言,与其说是为了证明目的,不如说是防篡改。为了未来我。就像明天,或者今天稍后。命名某件事并非总是那么容易(通常是不可能的),以便使它的所有内容立即显而易见。范围限制对此非常有帮助。极大地。没有足够的理由不限制范围的静态变量。是的,有时您可能会意识到需要在功能之外访问它。因此,如果发生这种情况,请重构并给它更大的名称,更多的文档和更大的范围。
Shavais

33

2004年的MSDN博客文章:为什么C#不支持静态方法变量?处理原始帖子中提出的确切问题:

C#没有此功能有两个原因。

首先,通过具有类级别的静态变量,可以获得几乎相同的效果,而添加方法静态变量将需要增加复杂性。

其次,方法级静态变量在重复调用代码或从多个线程调用代码时会引起问题,因此臭名昭著,并且由于定义位于方法中,因此很难找到这些定义。

[作者:埃里克·甘纳森(Eric Gunnerson)

(Microsoft自己的存档中有相同的博客条目。Archive.org保留了注释。Microsoft的存档中没有。)


23
我了解这不一定是您的意见,但可以为类级静态变量提供完全相同的论点。“多线程是困难且容易出错的”,“多个用户在内存中修改相同的静态值更容易发生错误并且难以跟踪”,这并不是没有包含功能的借口。这些是现有功能集的问题,此功能并未特别加剧。为了上帝的缘故,它们具有静态类和静态构造函数,它们甚至因难以追踪甚至更难以进行单元测试而闻名。
Gurgadurgen '16

30

状态通常是对象的一部分或类型的一部分,而不是方法的一部分。(当然,例外是捕获的变量。)

如果要等效于局部静态变量,请创建一个实例变量或一个静态变量-并考虑方法本身是否实际上应该是具有该状态的其他类型的一部分。


5
请注意,在C ++中,可以将静态“变量”用于常量值。由于C#实际上仅具有数学意义上的常量,因此您将获得非常麻烦的方式来使用局部的,计算的常量(例如sqrt为2,仅作为示例)。
greenoldman'4

5
这两个选项都是解决方法,因为它们不仅具有本地功能,而且具有更高的可见性。创建一个新类是“更清洁”的解决方案,但是现在您需要更多代码来解决缺少的功能。
2012年

4
@JimBalter:我还是不同意。状态固有地与类型相关联(例如,状态存在于AppDomain中,而在类型初始化之前就不存在)。不过,如果能在没有人为攻击的情况下表达意见分歧,我将不胜感激
乔恩·斯基特

3
只是说,我处于需要仅在一个方法中使用的静态变量的情况下,并且在类定义中创建该变量将导致(不需要的)意大利面条代码。
MasterMastic 2013年

3
@肯:但是我的意思是,这本身就是设计的味道。如果编译器支持此功能,则必须将变量提升为实例或静态变量-因此无论如何它都将成为类或实例状态的一部分。如果这对您的班级不合适,那么这是不合适的,但是它在源代码中表示。如果它适当的,你为什么要隐瞒状态?
乔恩·斯基特

18

我对C的了解不像对C#那样熟悉,但是我相信您可以通过使用仅用于一种方法的类级静态来完成使用本地静态可能完成的所有工作。显然,这带来了一些语法上的变化,但是我相信您可以获得所需的任何功能。

此外,埃里克·利珀特(Eric Lippert)在他的博客上经常回答类似这样的问题。通常以这种方式回答:“一直有人问我:“为什么C#不实现X功能?”。答案总是一样的:因为没有人设计,指定,实现,测试,记录和发布该功能。 ” 从本质上讲,他的回答通常归结为,添加任何功能都需要花费金钱,因此,许多潜在功能没有实现,因为它们没有体现在成本效益分析的积极方面。


20
我自己不能说的更好。
Eric Lippert

2
用类静态实现局部静态的问题是类名称空间被不必要地污染了。向C#添加功能所需的资源问题是,为什么Microsoft的大多数语言(以及其他语言,例如cough,Sun / Oracle)都不尽人意。他们毫不犹豫地做“足够好”而不是好的事。
痴呆的刺猬

2
我可以尊重这个答案的第二部分。第一部分是解决方案,但不是借口。这是重要的部分,在这里。正如其他人所说的那样,说“您可以通过完成Y来完成X”与“您可以使用图灵机模拟任何编程语言的所有功能”没什么不同。但是我们没有这样做,因为尽管它是“功能强大的工具”,但仅具有与图灵机相同的功能(当然,除了无限内存之外)的语言还不够高效。
Gurgadurgen '16

我相信,通过使用仅用于一种方法的类级别的静态方法,您可以使用本地静态方法完成所有工作。 - 稻草人。每个人都知道这一点,但这是完全无关紧要的,因为它没有解决人们为什么要使用方法局部静态变量的原因-词法局部性是一个重要原则,许多后继者都对此加以强调。第二段就像virtus dormitivale meilleur des mondes的可能性-通用的手工挥舞着,没有解决这个问题。(自EL离开团队以来,C#幸运地添加了许多出色的功能。)
Jim Balter

5

因此,您想在方法中使用静态局部变量吗?恭喜你!您朝着成为一名真正的程序员又迈出了一步。

不要听所有的人告诉您静态本地人不是“干净”的,它们会阻碍“可读性”,并可能导致细微而难以发现的“错误”。废话!他们只是说,因为他们是想当程序员!他们中的许多人甚至可能在业余时间玩着一种深奥的函数式编程语言。你相信吗?一堆时髦!

真正的程序员拥抱范式我喜欢叫SDD -小号IDE效应d里文d ESIGN。以下是一些最重要的法律:

不可预测!永远不要从方法中两次返回相同的东西-即使使用完全相同的参数调用它!

螺丝的纯度-让我们变脏!状态本质上是渴望改变的,因为它是一类多聚内源性永不满足的类四面体,即它喜欢被尽可能多的合作者所感动。千万不要错过机会,帮个忙!

当然,在以副作用驱动方式进行编码的工具中,有静态局部变量。但是,正如您所注意到的,C#不支持它们。为什么?因为在过去的二十年中,Microsoft被所谓的Clean Codes所渗透,Clean Coders主张维护性优于灵活性和控制性。您甚至还记得上次看到我们心爱的蓝屏吗?现在猜猜是谁的错!

但是不要害怕!真正的开发人员不必承受那些糟糕的设计决策。如前所述,在lambdas的帮助下,局部变量可能是静态的。

但是,提供的解决方案并不十分令人满意。使用前面的答案,我们几乎符合SDD的代码看起来像这样:

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

要么

var zero = Increment()();

但这只是愚蠢的。即使是一个崇拜者开发人员可以看到,Increment()是不正常的方法,将得到可疑。一个真正的程序员,在另一方面,可以使其更加SDD样。他(她)知道,我们可以通过赋予属性类型来使属性或字段看起来像方法Func<T>!我们只需要通过执行一个lambda来初始化它,该lambda会依次初始化计数器并返回另一个lambda来增加捕获的计数器!

这是正确的SDD代码:

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

(您认为上述有点像IIFE吗?是的,您是对的,应该为自己感到羞耻。)

现在,每次调用Increment() 它都会返回不同的结果

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

当然,您也可以这样做,以使计数器在您的实例的生存期内生存下来

这会让他们想当程序员!


5
这应该是对这个问题的认真回答吗?感觉更像是一个古老的问题,被用来作为谴责“其他人”不良编程习惯的平台。
Claies

1
@Claies,当然,这很严重!这是显示如何正确模拟静态局部变量的唯一答案,因此从外部几乎无法分辨出常规C#方法和管理静态局部C#的“方法”之间的区别。
晚安书呆子自豪

1
这是HIL arious!我从未想过像这样的方法伪装属性……可能是因为这是您甚至想要的少数几个情况之一
AustinWBryan

1
@GoodNightNerdPride我尝试了您的解决方案,我真的很喜欢。不幸的是,它不适用于引用类型(例如List<>,我初始化为的等等null)。有什么办法可以使它起作用?在Func总回报率null,即使我改变在调用方法返回的列表Func。可以这么说,我以为参考会“坚持”。
Silkfire

1
@silkfire因此,您想从外部插入“静态局部变量”吗?这就是SSD精神!但是开个玩笑:C#引用类型是对对象的引用,而不是对引用的引用!您基本上想做的是:dotnetfiddle.net/wd73MS注意第二个打印仍然显示旧计数,因为列表引用无法从外部重定向到新列表。但是,您可以通过调用发生变异的名单Add()Remove()等就可以了。
晚安书呆子骄傲

2

C#是一种面向组件的语言,没有类或局部方法范围之外的变量概念。方法中的变量也不能声明为静态,因为您可能习惯于在C语言中进行操作。但是,始终可以将类静态变量用作替代变量。

通常,通常有一些方法可以解决C#中的编程问题,而无需使用方法级静态方法。状态通常是应该设计为类和类型的东西,而不是方法。


我必须说,拥有它可能真的很不错: static object myLock = new object(); lock(myLock){ ... }
Benji Altman

1

从逻辑上讲,是的。它与仅在该方法中使用的类级静态成员相同。但是,方法级静态成员将被更封装。如果存储在成员中的数据仅打算由单个方法使用,则只能由该单个方法访问。

但是,通过创建嵌套类,您可以在C#中获得几乎完全相同的效果。


嵌套类可以访问嵌套类的私有变量。然后,您可以将变量设为私有并将其中的一个方法放在其中,但是您仍然必须将其发送给嵌套类的实例。当然,您可以使其成为类扩展。那么没有人会是更明智的。
Lee Louviere 2012年

0

因为静态局部变量绑定到该方法,并且该方法在所有实例之间共享。

我必须更正自己和其他程序员,他们希望使用该方法在每个类实例中具有唯一性。

但是,如果将其设置为静态类或类的静态实例,则从语法上看,每个容器类是否有一个实例,或者根本没有一个实例。

如果您不使用它们,以后重构也将变得更加容易。


1
我认为“静态”一词应明确指出只有1个变量
JoelFan 2012年

在极少数情况下,您需要在方法内部存储一个仅1个变量,您必须问自己为什么要通过一堆方法分散共享类状态信息。
Lee Louviere 2012年

1
Lee:通常是因为您要处理重入或其他类似方法特定的问题。在这些情况下,将状态与方法关联而不是与整个类关联在逻辑上更有意义,即使CLR与普通私有静态变量在实现方式上没有内在的区别。
reirab

0

我认为通过为类创建公共静态字段可以轻松解决局部静态的想法。您认为逻辑上的变化很小吗?

如果您认为这将是一个很大的逻辑变化,那么我很想听听如何。

class MyClass
{
    public static float MaxDepthInches = 3;

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

7
这是两个重大的逻辑更改:1)它是公共的,因此可以通过类外部的代码进行修改。这意味着使用您的类的其他人可以用您的代码无法预测的方式对其进行修改。不用说,这通常是不好的。2)变量的范围是整个类,而不仅仅是一个方法。将范围从一个方法更改为整个类是一个很大的更改,即使您确实将其声明为私有也是如此。
reirab

0

我看不到局部静态有什么额外的好处,如果您将类保持单一目的且较小,那么全球静态污染几乎没有问题,因为反对者喜欢抱怨。但是,这只是另一种选择。

 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;
  }


 }

0

您可以使用嵌套类作为解决方法。由于C#将静态变量的范围限制为类,因此可以将嵌套类用作范围。

例如:

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

这里Foo支持Increment方法,但是它由IncrementInternal包含静态变量作为成员的私有嵌套类支持。当然,counter在的上下文(其他方法)中不可见Foo

顺便说一句,如果您想访问Foo内部的上下文(其他成员和方法)IncrementInternal.Increment,则可以将其this作为参数传递IncrementInternal.Increment给从中调用时Foo

为了使范围尽可能小,我的建议是为每个此类方法创建一个嵌套类。而且由于它可能不是很常见,因此嵌套类的数量将保持足够少以保持它的状态。

我认为它比匿名函数或IIFE更干净。

您可以在此处观看现场演示。


-5

如果您可以想象某种形式的Lippert / Farnsworth混合实体向所有人宣布“好消息”!,C#6.0允许使用该using static语句。这有效地使您可以将静态类方法(似乎还包括属性和成员)导入全局范围。

简而言之,您可以执行以下操作:

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; }
    }
}   

虽然仅在C#6.0中可用,但是据我了解,生成的程序集应该与以前的.NET平台兼容(如果我错了,请更正我)。


6
这与问题无关。
JoelFan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.