为什么C#没有像C这样的本地静态变量?我想念!!
答案 0 :(得分:113)
因为他们搞砸了,并且遗漏了一个适合自己的有用功能。
关于你应该如何编码以及什么是聪明的,你应该重新考虑你的生活方式的所有争论都是浮夸的防御借口。
当然,C#是纯粹的,而且是以人为本的。这就是他们为lambda函数自动生成持久本地的原因。这一切都很复杂。我觉得很蠢。
在许多情况下,循环范围静态是有用且重要的。
简短而真实的答案是,您必须将本地静态移动到类范围内,并在C#中使用类命名空间污染。把你的投诉带到市政厅。
答案 1 :(得分:30)
状态通常是对象的一部分或类型的一部分,而不是方法的一部分。 (当然,例外是被捕获的变量。)
如果你想要一个本地静态变量的等价物,要么创建一个实例变量,要么创建一个静态变量 - 并考虑该方法本身是否应该实际上是该状态的不同类型的一部分。
答案 2 :(得分:27)
MSDN博客文章 Why doesn't C# support static method variables? 处理原帖中提出的确切问题:
C#没有这个功能有两个原因。
首先,通过拥有一个可以获得几乎相同的效果 类级静态,并且需要增加方法静态 复杂性。
其次,方法级别的静态因素有些臭名昭着 重复调用代码或从多个线程调用代码时出现的问题 由于定义在方法中,因此很难找到 定义
[作者:Eric Gunnerson]
答案 3 :(得分:15)
我对C语言并不熟悉,因为我是C#,但我相信通过使用仅用于一种方法的类级别静态,您可以使用本地静态完成所有操作。显然,这会带来一些语法上的改变,但我相信你可以得到你需要的任何功能。
此外,Eric Lippert在他的blog上回答了这样的问题。一般在this way回答:“我被问到”为什么C#不能实现X特征?“所有的时间。答案总是一样的:因为没有人设计,指定,实施,测试,记录和发货这个特点。“基本上他的答案通常归结为,添加任何功能都需要花钱,因此,许多潜在的功能都没有实施,因为它们没有在成本效益分析的积极方面出现。
答案 4 :(得分:7)
所以你想在方法中使用静态局部变量?恭喜!您向成为真正的程序员迈出了一步。
不要听所有人告诉你静态当地人不干净"他们阻碍"可读性"并且可能导致微妙且难以发现的错误"。废话!他们只是说因为他们是想要程序员!他们中的许多人甚至可能在他们的空闲时间使用深奥的函数式编程语言来玩游戏。你相信吗?真是一堆赶时髦的人!
真正的程序员接受一种范例我喜欢称之为 SDD - S ide效果 D 撕裂的 d 强> ESIGN 。以下是一些最重要的法律:
不可预测!永远不要从方法中返回相同的东西两次 - 即使它被使用完全相同的参数调用!
螺丝纯度 - 让它变脏!状态,天生就渴望改变,因为它是insatiable monoid in the category of polyamorous endofunctors,即它喜欢被许多合作者所感动可能。永远不要错过这个有利于它的机会!
用于以副作用驱动方式编码的工具当然是静态局部变量。但是,正如您所注意到的,C#不支持它们。为什么?因为在过去的二十年里,微软已经被所谓的 Clean Coders 渗透,这有利于可维护性而不是灵活性和控制力。你能记得你最后一次看到我们心爱的蓝屏吗?现在猜猜是谁的错!
但不要害怕! 真正的开发人员不必遭受那些糟糕的设计决策。如前所述,在lambdas的帮助下,局部变量可能是静态的。
但是,提供的解决方案并不令人满意。使用previous answer我们几乎符合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()
it will return something different时:
var zero = Increment();
var one = Increment();
当然你也可以这样做counter survives the lifetime of your instance。
那将向他们展示想要程序员!
答案 5 :(得分:1)
C#是一种面向组件的语言,并没有类或本地方法范围之外的变量概念。方法中的变量也不能声明为静态,因为您可能习惯于在C中进行操作。但是,您始终可以使用类静态变量作为替代。
作为一般惯例,通常有一些方法可以解决C#中的编程问题而无需使用方法级静态。状态通常是你应该设计成类和类型的东西,而不是方法。
答案 6 :(得分:1)
逻辑上,是的。它与仅在该方法中使用的类级静态成员相同。但是,方法级静态成员将更加封装。如果存储在成员中的数据仅用于单个方法,则只能通过该单个方法访问。
但是,通过创建嵌套类,您可以在C#中实现几乎完全相同的效果。
答案 7 :(得分:0)
我认为通过为类创建公共静态字段可以轻松解决局部静态的问题。你认为没有什么逻辑变化吗?
如果你认为这将是一个很大的逻辑变化,我会有兴趣听听如何。
class MyClass
{
public static float MaxDepthInches = 3;
private void PickNose()
{
if (CurrentFingerDepth < MyClass.MaxDepthInches)
{
CurrentFingerDepth++;
}
}
}
答案 8 :(得分:0)
因为静态局部变量与方法相关联,并且该方法在所有实例之间共享。
我必须纠正自己和其他程序员,他们希望使用该方法对每个类实例都是唯一的。
但是,如果你使它成为一个静态类或一个类的静态实例,那么从语法上来说,每个容器类是一个实例,还是一个实例。
如果你不使用它们,以后也会更容易重构。
答案 9 :(得分: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更干净。
您可以观看现场演示here。
答案 10 :(得分: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;
}
}
答案 11 :(得分:-3)
您可以使用委托来模拟它...这是我的示例代码:
public Func<int> Increment()
{
int num = 0;
return new Func<int>(() =>
{
return num++;
});
}
您可以这样称呼它:
Func<int> inc = Increment();
inc();
答案 12 :(得分:-3)
如果您能想象某种Lippert / Farnsworth混合实体宣布GOOD NEWS EVERYONE!,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平台兼容(如果我错了,请纠正我)。