use*_*398 36 c# language-design
我是C#的新手,来自C++背景.在C++中,您可以这样做:
class MyClass{
....
};
int main()
{
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
}
Run Code Online (Sandbox Code Playgroud)
而在C#中:
class Program
{
static void Main(string[] args)
{
Car x;
x.i = 2;
x.j = 3;
Console.WriteLine(x.i);
Console.ReadLine();
}
}
class Car
{
public int i;
public int j;
}
Run Code Online (Sandbox Code Playgroud)
你不能这样做.我想知道为什么Car x不做它的工作.
Eri*_*ert 47
这里存在很多误解,无论是在问题本身还是在几个答案中.
让我首先研究一下这个问题的前提.问题是"为什么我们需要newC#中的关键字?" 问题的动机是C++的这个片段:
MyClass object; // this will create object in memory
MyClass* object = new MyClass(); // this does same thing
Run Code Online (Sandbox Code Playgroud)
我基于两个理由批评这个问题.
首先,这些在C++中没有做同样的事情,所以问题是基于对C++语言的错误理解.理解C++中这两件事之间的区别是非常重要的,所以如果你不清楚什么是差异,找一个可以教你如何知道差异是什么以及何时使用每个东西的导师.
其次,问题预设 - 错误地 - 这两个语法在C++中做同样的事情,然后,奇怪的是,问"为什么我们需要new在C#中?" 当然,在这个问题上提出正确的问题 - 再次,错误 - 预设是"为什么我们需要new用C++?" 如果这两个语法做同样的事情 - 他们没有 - 那么为什么首先有两个语法?
所以这个问题都是基于错误的前提,而关于C#的问题实际上并没有遵循 - 误解 - C++的设计.
这是一团糟.让我们抛出这个问题并提出一些更好的问题.让我们问一下关于C#qua C#的问题,而不是在C++的设计决策环境中.
new X运算符在C#中做了什么,其中X是类或结构类型?(为了讨论的目的,让我们忽略代理和数组.)
新运营商:
好吧,我已经可以听到C#程序员的反对意见,所以让我们解雇他们.
异议:如果类型是值类型,则不会分配新的存储空间,我听到你说.好吧,C#规范不同意你的看法.当你说
S s = new S(123);
Run Code Online (Sandbox Code Playgroud)
对于某些结构类型S,规范说新的临时存储在短期池上分配,初始化为其默认值,构造函数运行时使用thisset来引用临时存储,然后将生成的对象复制到s.但是,允许编译器使用复制省略优化,前提是它可以证明在安全程序中无法观察到优化.(练习:在什么情况下不能执行复制省略;如果有或没有使用elision,请举例说明会有不同行为的程序.)
异议:可以使用以下方式生成值类型的有效实例default(S); 没有构造函数被调用,我听你说.那是对的.我没有说这new是创建值类型实例的唯一方法.
事实上,对于价值型new S()和default(S)有同样的事情.
异议:new S()我听到你说,如果构造函数真的被执行过,如果不存在于C#6的源代码中.这是"如果一棵树落在森林里,没有人听到它,它会发出声音吗?" 题.对没有做任何事情的构造函数的调用与根本没有调用之间是否有区别?这不是一个有趣的问题.编译器可以自由地忽略它知道什么都不做的调用.
假设我们有一个值类型的变量.我们必须使用由
new?生成的实例初始化变量吗?
否.自动初始化的变量(如字段和数组元素)将初始化为默认值 - 即结构的值,其中所有字段本身都是其默认值.
显然,正式参数将使用参数初始化.
需要值类型的局部变量与被明确分配的东西被读领域之前,但它不一定是一个new表达式.
如此有效,值类型的变量会自动初始化,相当于
default(S),除非它们是本地人?
是.
为什么不为当地人做同样的事呢?
使用未初始化的本地与错误代码密切相关.C#语言不允许这样做,因为这样做会发现错误.
假设我们有一个引用类型的变量.我们必须
S使用由...生成的实例进行初始化new吗?
不会.自动初始化变量将使用null初始化.可以使用任何引用初始化本地,包括null,并且必须在读取之前明确分配.
如此有效,引用类型的变量会自动初始化
null,除非它们是本地人?
是.
为什么不为当地人做同样的事呢?
同样的道理.一个可能的错误.
为什么不通过自动调用默认构造函数自动初始化引用类型的变量?也就是说,为什么不做
R r;同样的R r = new R();?
嗯,首先,许多类型没有默认构造函数,或者就此而言,根本没有任何可访问的构造函数.其次,对于未初始化的本地或字段有一个规则,对于正式的另一个规则,以及对于数组元素的另一个规则,似乎很奇怪.第三,现有规则非常简单:必须将变量初始化为一个值; 这个价值可以是你喜欢的任何东西; 为什么假设需要一个新实例?如果这样,那将是奇怪的
R r;
if (x) r = M(); else r = N();
Run Code Online (Sandbox Code Playgroud)
导致构造函数运行以初始化r.
撇开语义中的
new运营商,为什么是必要的语法能有这样的操作?
不是.有许多可以语法化的替代语法.最明显的是简单地new完全消除.如果我们有一个C带有构造函数的类,C(int)那么我们可以简单地说C(123)而不是new C(123).或者我们可以使用类似C.construct(123)或类似的语法.没有new操作员,有很多方法可以做到这一点.
那为什么呢?
首先,C#被设计为C++,Java,JavaScript和其他语言的用户立即熟悉,这些语言new用于指示正在为对象初始化新存储.
其次,非常需要正确的句法冗余水平.对象创建很特别; 我们希望在它自己的运营商发生时呼叫.
Dmi*_*nko 28
在C#中你可以做类似的事情:
// please notice "struct"
struct MyStruct {
....
}
MyStruct sample1; // this will create object on stack
MyStruct sample2 = new MyStruct(); // this does the same thing
Run Code Online (Sandbox Code Playgroud)
回想一下像int,double和bool,也是类型的原语struct,所以即使它是常规的写
int i;
Run Code Online (Sandbox Code Playgroud)
我们也可以写
int i = new int();
Run Code Online (Sandbox Code Playgroud)
与C++不同,C#不使用指针(在安全模式下)到实例,但是C#具有class和struct声明:
class:你有引用实例,内存是在堆上分配的,
new是强制性的 ; 类似于MyClass*在C++
struct:你有价值,内存(通常)在堆栈上分配,
new是可选的 ; 类似于MyClass在C++
在你的特殊情况下,你可以Car变成struct
struct Car
{
public int i;
public int j;
}
Run Code Online (Sandbox Code Playgroud)
所以片段
Car x; // since Car is struct, new is optional now
x.i = 2;
x.j = 3;
Run Code Online (Sandbox Code Playgroud)
会是对的
Mat*_*nen 15
在C#中,class类型对象总是在堆上分配,即这些类型的变量总是引用("指针").只声明这种类型的变量不会导致对象的分配.class像在C++中常见的那样在堆栈上分配对象(通常)不是C#中的选项.
未分配的任何类型的局部变量都被视为未初始化,并且在分配给它们之前无法读取它们.这是一个设计选择(另一种方式是default(T)在声明时分配给每个变量),这似乎是一个好主意,因为它应该保护您免受某些编程错误的影响.
它类似于在C++中如何说出来SomeClass *object;并且从不为它分配任何东西是没有意义的.
因为在C#中所有class类型变量都是指针,所以在声明变量时分配空对象会导致代码效率低下,而实际上只是想稍后为变量赋值,例如在以下情况中:
// Needs to be declared here to be available outside of `try`
Foo f;
try { f = GetFoo(); }
catch (SomeException) { return null; }
f.Bar();
Run Code Online (Sandbox Code Playgroud)
要么
Foo f;
if (bar)
f = GetFoo();
else
f = GetDifferentFoo();
Run Code Online (Sandbox Code Playgroud)
zac*_*caj 12
忽略堆栈与堆栈的事情:
因为C#做出了错误的决定来复制C++,因为他们应该刚刚制作语法
Car car = Car()
Run Code Online (Sandbox Code Playgroud)
(或类似的东西).拥有'新'是多余的.
当您在此语句中使用引用的类型时
Car c = new Car();
Run Code Online (Sandbox Code Playgroud)
创建了两个实体:一个名为c堆栈中Car类型的对象的引用,以及堆中类型为Car的对象.
如果你只是写
Car c;
Run Code Online (Sandbox Code Playgroud)
然后你创建一个未初始化的未初始化引用(假设c是一个局部变量).
实际上它等同于C++代码,而不是引用使用指针.
例如
Car *c = new Car();
Run Code Online (Sandbox Code Playgroud)
要不就
Car *c;
Run Code Online (Sandbox Code Playgroud)
C++和C#之间的区别在于C++可以在堆栈中创建类的实例
Car c;
Run Code Online (Sandbox Code Playgroud)
在C#中,这意味着创建Car类型的引用,就像我说的那样无处可寻.
| 归档时间: |
|
| 查看次数: |
14383 次 |
| 最近记录: |