"C++ void Pointer" 和 "C# var" 之间的区别

Difference between "C++ void Pointer" and "C# var"

在学习C#的过程中,我想到了这个问题。 voidvar 有什么区别? 这是我想分享的两个例子:

void * voidInt = (void *) 7;
void * voidChar = (void *) 'F';
void * voidCharArray = (void *) "AbcString";

这是 var 的示例:

var varInt = 7;
var varChar = 'F';
var varCharArray = "AbcString";

谁能帮我解决这个问题?

voidvar 没有任何共同点:

  • void(C 和 C++ 中的指针变量所使用的)表示未指定(不是确定的)类型。 void* 在托管 C# 中是不允许的*(尽管非常弱的类型,例如 object 引用可能是一个近似值)。通常,void* 类型需要重新转换才能使用。

  • 但是voidreturn类型来自方法/函数的意思相同 在这两种语言中,这是为了传达 没有 return 值 (就像 Scala 中的 Unit

  • 相比之下,C# 中的 var 定义了一个 implicitly typed variable - 变量仍然具有强类型,但实际类型是在编译时从右侧推断的。

例如

var v1 = "Foo"; // v1 is a string, because it is inferred from the right hand side
var v2 = XDocument.Parse(@"c:\temp\foo.xml"); // v2 is the return type of the function
使用匿名类型时通常需要

var - 这可能是您在 var 和匿名类型之间建立联系的地方:

var v3 = new { Name = "Foo", Value = 123};  // v3 is strongly typed, anonymous class.

var 对于将变量分配给 LINQ 表达式的 return 值特别有用,其中类型可能非常复杂:

var v3 = db.Persons
      .Join(db.Cities, p => p.CityId, c => c.Id, (p, c) => new {Person = p, City = c})
      .GroupBy(pc => pc.City.Name);

* 实际上,这并不完全正确,您可以在 C# 中使用 void* 和 unsafe

编辑

另外值得一提的是,从 C#6 开始,隐式 var 类型只能用于局部变量,即 C# 不支持方法 return 类型的隐式类型(不像 Scala 这样的函数式语言,在大多数情况下编译器也可以推断出方法的 return 类型)。

你可以用 c++ void pointer 来做到这一点:

void * val = (void *) 7;
val = (void *) "Abcd";

但是你不能用 c# var:

var val = 7;
val = "abcd";

这将引发错误。

更新

如果您想实现与 void * 类似的行为,您可以使用 dynamic

dynamic val = (dynamic) 7;
val = (dynamic) "ABC";

当使用var时,变量的实际类型在编译时确定。但是,当使用 dynamic 时,变量的实际类型在 运行 时确定。

  • 在不安全的上下文中,C# 中 C++ void* 的等价物是 void*。任何数据指针类型都可以分配给 void*.
  • 在安全上下文中,object 是(松散地)对应的概念。任何 class/interface/struct 个实例都可以分配给它。
  • C++ 中 C# var 的等价物是 auto。当用于声明和初始化局部变量时,如果可能的话,它充当分配给该变量的表达式的类型。

这里的其他答案都很好,但我认为他们没有弄清楚基本原理。这是您感到困惑的基本原理,所以让我们解决这些问题。

  • 一个变量是一个包含存储位置
  • 变量类型关联。
  • 一个局部变量有一个名称

所以voidIntvoidCharvoidCharArrayvarIntvarCharvarCharArray都是变量,它们都有 类型 与之关联。每个变量都可以分配该类型的值或产生该类型的值,具体取决于变量是被写入还是从中读取.

好的,那么什么是指针?

  • 一个类型有对应的指针类型。 (请注意,在不安全的 C# 中,只有 非托管类型 具有相应的指针类型。)
  • void *类型是一种特殊的指针类型。
  • 一个指针是一个值。
  • 类型 T* 的指针可以 取消引用 以生成类型 T 变量 T* 不能是 void*.
  • 指针可以显式转换为任何整数类型或从任何整数类型转换,尽管这些操作允许丢失信息并且取决于实现细节。
  • 任何指针值都可以隐式转换为 void*
  • 任何 void* 值都可以显式转换为任何指针类型值。

C# 中的 var 是什么?

  • var 是一个 "syntactic sugar",它告诉编译器从 initialzier 推断变量的类型,而不是要求它被写出来。

C#中的"anonymous types"是什么?

  • C# 中的某些表达式具有未声明且没有名称的类型;这些被称为 "anonymous" 类型。

所以现在我们可以查看您的程序并查看每一行的作用。

void * voidInt = (void *) 7;

voidIntvoid* 类型的变量。分配给它的值是整数 7 到指针的转换,这几乎可以肯定是任何现代操作系统上的垃圾指针。这段代码本质上是无意义的。

更合理的代码是:

int myInt = 7;
int* intPtr = &myInt;
void* voidInt = intPtr;

这意味着myInt是一个变量,它包含值7intPtr是一个变量,它包含一个指针;当取消引用该指针时,它会生成变量 myIntvoidInt 是一个变量,可以保存任何指针,从 intPtr 读取的值是一个指针。所以现在 voidIntintPtr 都持有一个指向变量 myInt.

的指针
void * voidChar = (void *) 'F';

这里也是一样。字符 F 被视为数字并转换为指针值,存储在变量中。这是不明智的。明智的代码应该是这样的:

char myChar = 'F';
void *voidChar = &myChar;

但这很有道理:

void * voidCharArray = (void *) "AbcString";

C++ 中的字符串文字可转换为 char*,它是指向第一个字符存储的指针,并且该指针可转换为 void*

这个呢?

var varInt = 7;
var varChar = 'F';
var varCharArray = "AbcString";

这只是一种愉快的写作方式

int varInt = 7;
char varChar = 'F';
string varCharArray = "AbcString";

每个变量都有其给定的类型,每个赋值都会在变量中存储该类型的值。

匿名类型呢?

var anon = new { X = 123, Y = 456 };

这将创建一个匿名类型的变量,其中匿名类型具有两个类型为 int 的属性 X 和 Y。该类型没有名称,因此无法在声明中写出类型,因此必须使用 var

这里的关键是确保您掌握了基础知识:指针是,它们可能是取消引用,这样做会产生一个变量。由于指针是 values,它们本身可能存储在指针类型的 variables 中。这几乎与 var 无关,后者是 C# 中让编译器确定变量类型的一种令人愉快的方式。