C#中简洁字段初始化的提示



我正在学习C#;我的经验是使用C++和Java。我想知道在声明和初始化一些相关字段时,是否有一个技巧可以用来减少"结结巴巴"(在多个地方重复相同的代码)?

以下是我想做的一个琐碎的例子(使用UrhoSharp 3D库):

public class MyScene
{
Scene scene = new Scene();
Node camera = scene.CreateChild("Camera");
Node light = scene.CreateChild("Light");
Node model = scene.CreateChild("Model");
// etc
}

但是,这不会编译,因为Node字段初始化器"无法引用非静态字段"scene。最简单的解决方案似乎是:

public class MyScene2
{
Scene scene = new Scene();
Node camera, light, model;
MyScene2()
{
camera = scene.CreateChild("Camera");
light = scene.CreateChild("Light");
model = scene.CreateChild("Model");
}
// etc
}

对于每多出一个Node

我就需要多出两行代码我需要编辑两行代码(编辑:根据Gusman的建议,将声明折叠成一行)有什么技巧可以让它更简洁吗?

在最新版本的C++中,第一个代码片段可以工作——字段按照严格的顺序初始化,并且可以引用以前的字段。在Java中,我可以将scene放在外部类中,并将Node字段放在内部类中,这样它们就可以引用它,但C#对内部类没有相同的概念。(当然,我并不是说这些语言"更好",它们只是不同,我对它们更熟悉。)

对于这个特定的例子,我可能会让MySceneScene继承,但我非常小心引入继承只是为了缩短代码!我可能需要一个更复杂的节点树,而不是所有的节点都是scene的直接子节点。所以我想知道是否有任何事情可以纯粹在字段初始化级别上完成。

有趣的是,C++/CLI确实支持"简洁"语法:

ref struct MyScene
{
Scene^ scene = gcnew Scene();
Node^ camera = scene->CreateChild("Camera");
Node^ light = scene->CreateChild("Light");
Node^ model = scene->CreateChild("Model");
};

您甚至可以将initonly(C#中的"readonly")添加到每个字段中。

编辑:您可以使用属性和表达式体成员获得所需的简洁性。

sealed class MyScene2
{
public Scene scene => new Scene();
public Node camera => scene.CreateChild("Camera");
public Node light => scene.CreateChild("Light");
public Node model => scene.CreateChild("Model");
}

但是,每次访问该属性时,他的代码都会调用CreateChild()

到目前为止,我能为一次性字段初始化想出的最简洁的方法是使用C#的元组:

public class MyScene
{
static (Scene, Node, Node, Node) f(Scene scene)
{
return (scene, scene.CreateChild("Camera"), scene.CreateChild("Light"), scene.CreateChild("Model"));
}
readonly (Scene scene, Node camera, Node light, Node model) t2 = f(new Scene());
};

那只长了一行。。。

如果简洁性是您主要关心的问题,并且您希望以同样的方式初始化一堆私有字段,为什么不这样做呢:

public class Scenery {
Dictionary<String, Node> children = new Dictionary<String, Node> {
{"Camera", null}, {"Light", null}, {"Model", null}};
Scene scene = new Scene();
public Scenery() {
foreach (var s in children.Keys) {
children[s] = scene.CreateChild(s);
}
}
}

这使您可以根据需要轻松地添加或减去字段。

下面是一个使用嵌套类实现所需功能的示例。

public  class ShinyClass
{
class InnerVar
{
public static Random r = new Random();
}
decimal d1 = InnerVar.r.Next();
decimal d2 = InnerVar.r.Next();
decimal d3 = InnerVar.r.Next();
public override string ToString()
{
return d1 + " " + d2 + " " + d3;
}
}

更新:这是一个抓住时机的时刻。如果更改内部类中变量的值,它们的静态性质将影响所有ShinyClass对象。因此,例如,如果你在某个地方使r为null,然后另一个方法试图在另一个对象中使用它,这将不起作用。所以,如果你在我上面的ShinyClass中添加这样的东西:

public void KillRandom()
{
InnerVar.r = null;
}
public int UseVar()
{
return InnerVar.r.Next();
}

然后你试着做这样的事情:

var s = new ShinyClass().ToString();
new ShinyClass().KillRandom();
var e = new ShinyClass().UseVar();

s将是一个有效的结果,但尝试获取e将引发NullReferenceException。在这种情况下,这种方法没有帮助。

最新更新