什么是由字段初始化引起的有害代码爆炸的现实例子?

时间:2017-02-01 18:59:06

标签: c# .net

CLR中通过C#,Richter注意到在类声明中初始化字段,如此

class C {
  int x = 3;
  int y = 4;

  public C() { ... }
  public C(int z) { ... }

  ...
}

导致在每个构造函数的开头插入语句,将语句设置为提供的值。因此,上面的行int x = 3;将负责两个单独的初始化 - 一个在无参数构造函数中,另一个在构造函数中接受int参数。

里奇继续说:

  

这意味着你应该知道代码爆炸[...]如果你有几个初始化的实例字段和很多重载的构造函数方法,你应该考虑在没有初始化的情况下定义字段,创建一个执行代码的构造函数常见的初始化,并让每个构造函数显式调用公共初始化构造函数。这种方法将减少生成代码的大小。

我很难想象这会成为一个明显的问题,这让我想知道我是否在这里遗漏了一些东西。例如,如果我们想象我们的类有十个构造函数和一百个字段,并且需要16个字节的本机机器代码来执行初始化,那么我们讨论的是总共16 kB生成的本机代码。当然,本世纪任何计算机上的内存都可以忽略不计,对吗?

我认为使用泛型可以将其乘以一个小因子,但对工作集的影响似乎仍然很小。

问题:我在这里遗漏了什么,如果是的话,那是什么?

虽然我的问题主要是理论上的 - 我想测试我自己的理解 - 它也有点实用,因为初始化它们被声明的字段似乎比使用像Richter建议的集中式构造函数产生更可读的代码

2 个答案:

答案 0 :(得分:3)

  

由字段初始化引起的有害代码爆炸的现实例子是什么?

我不知道这个问题的任何现实例子。

  

我很难想象这会成为一个引人注目的问题

我也是。

  

这让我想知道我是否在这里遗漏了一些东西。

据我所知。

  

本世纪任何计算机上的内存都可以忽略不计,对吗?

右。而且也不要忘记你在一个jitted的世界里买单。你只能得到尽可能多的ctors。

  

我想使用泛型可以乘以一个小因子

对于使用值类型构造的泛型类型,这通常是正确的。

  

但是对工作集的影响仍然很小。

同样,工作集是玩游戏。只被调用几次的代码最终被调出,并且许多构造函数只在程序中被调用几次。

  

我在这里遗漏了什么,如果是的话,那是什么?

据我所知。

  

初始化他们声明的字段似乎比使用像Richter建议的集中式构造函数产生更多可读代码。

我会首先优化正确性和优雅,只有在经验性能测试清楚地表明这是一个真实的,影响用户的问题时才能解决这个问题。

答案 1 :(得分:2)

首先,我想指出这个答案的部分内容是从大学回来的99-01(处理程序代码而不是OOP)和我已经拿起的部分随着时间的推移,不一定与c#有关,所以我不确定与c#相关的确切数字,因此以〜开头的任何数字可能都不准确(对于构造函数16b,我会假设它是'#s;实际上每个参数12b + 1b虽然可能不是c#的情况,但是为了演示目的它们并不重要。

通常在创建对象时,您将知道需要哪些信息,并能够创建一个空白模板或提供一些值来生成您希望通过链接构造函数来构建它的内容,但是如果在创建实例时您不能做什么? ;知道所有的价值观还是不能链接你的构造函数?下面的示例显示了一个类,该类将根据您为其提供的值创建集合设计对象的实例。

class ObjOfSetDesign
{
 int A, B, C, D, E, F;
 int X = 90;
 int Y = 45;
 int Z = 75;

public ObjOfSetDesign(int a)
{
 A = a;
 D = a / 5;
 B = (A + D) * 2;
 C = B * 6;
 E = A * 4;
 F = C / 2;
}

public ObjOfSetDesign(int b, int c)
{
 B = b;
 C = c;
 D = b / 12;
 A = D * 5;
 E = A * 4;
 F = c / 2;
}

public ObjOfSetDesign (int d, int e, int f)
{
 D = d;
 E = e;
 F = f;
 A = d * 5;
 C = f * 2;
 B = c / 6;
}

显然这不是最好的例子,你可以计算A并且每次都使用那个构造函数,但是如果你的类有100个字段或者某些变量使用XYZ来计算它们的值,那么你可能没有那么奢侈。但是,除了值可用于计算其他值之外,没有任何构造函数具有任何共同点,在第二个构造函数的情况下,您甚至不能使用b和c来调用:this(a)。< / p>

认为这并不是所有这些字段都可能只是整数,你可能有列表或将一个自定义类的实例传递给构造函数来创建你的对象,我希望这可以帮助你看到一个类如何有100个字段,10个独立的构造函数,每个构造函数的开头都会注入XYZ。

至于如何在你的例子中达到16kb,这将涵盖创建模板的代码,但AFAIK也必须自己保存数据。如果你的一半字段被初始化为一个额外100b的整数,但是如果你有一个列表,你将有~22b来创建列表,1b每个索引加上其中包含的数据。如果您的列表包含列表,则同样适用于每个列表,这使我可以参加下一课程。

class BiggerObj
{
 ObjOfSetDesign Obj1 = new ObjOfSetDesign(5);
 ObjOfSetDesign Obj2 = new ObjOfSetDesign(10);
 ObjOfSetDesign Obj3 = new ObjOfSetDesign(15);

 ObjOfSetDesign Obj4;
 ObjOfSetDesign Obj5;

 public BiggerObj(int a4, int a5)
 {
  Obj4 = new ObjOfSetDesign(a4);
  Obj5 = new ObjOfSetDesign(a5);
 }

 public BiggerObj(int b4, int c4, int b5, int c5)
 {
  Obj4 = new ObjOfSetDesign(b4, c4);
  Obj5 = new ObjOfSetDesign(b5, c5);
 }
 publ BiggerObj(int d4, int e4, int f4, int d5, int e5, int f5)
 {
  Obj4 = new ObjOfSetDesign(d4, e4, f4);
  Obj5 = new ObjOfSetDesign(d5, e5, f5);
 }
}

在这个例子中,我们的ObjOfSetDesign现在被注入我们的BiggerObj构造函数的开头以及创建它的代码,包括我们的3个构造函数和XYZ现在被注入9次。以下是我计算加起来的方法。

在ObjOfSetDesign中你有:

  • 9 fields = 9b
  • 3初始化ints = 6b
  • 3个构建体(~16b×3)= 48b + 18b注入= 66b

总计81b以创建一个包含18b数据的对象

在BiggerObj中你有:

  • 5场= 5b
  • 3初始化ObjOfSetDesign =(75b x 3)225b
  • 3个构建体(~16b×3)= 48b + 675b注入= 723b

总计953b以创建一个持有90b的对象

这甚至不包括在每个构造函数中调用函数的成本。如果在这两种情况下我们都调用了一个公共构造函数来初始化预设字段,那么ObjOfSetDesign将是73b而BiggerObj将达到69b,加上我们构造函数的代码大小(我没有包含它,因为它不会#&# 39;除了有一个额外的构造函数与原始的声明初始化方法相抵触之外,还要改变,并且仍然保留相同数量的数据。

在较大的范围内,例如你的100字段类,特别是如果像上面那样嵌套,这最终会导致一个对象插入&gt; 5mb的代码,当它实际上只需要在最多50kb。

希望这有帮助,如果这些不适用于c#,请告诉我,以便我能纠正。