C# 是否像 C++ 一样有指向成员的指针?
Does C# have pointers to members like in C++?
在 C++ 中,您可以编写以下代码:
int Animal::*pAge= &Animal::age;
Animal a;
a.*pAge = 50;
C# 中是否有类似的功能?
编辑: 澄清一下,我不是询问指针。我问的是 "pointers to members",C++ 中的一个特性,与 .*
和 ->*
运算符一起使用。
编辑 2:这是成员指向指针的用例示例。
假设我们有以下 class:
class Animal
{
int age;
int height;
int weight;
…
}
假设我们想要编写可以找到平均值的方法 age/height/weight/etc。数组中的所有动物。然后我们可以这样做:
int averageAge(Animal[] animals)
{
double average = 0;
for (…)
average += animals[i].age;
return average/animals.length;
}
int averageHeight(Animal[] animals)
{
//code here again
}
int averageWeight(Animal[] animals)
{
//code here again
}
我们最终会在此处复制和粘贴大量代码,如果我们寻找平均值的算法发生变化,我们将遇到维护噩梦。因此,我们希望为任何成员 抽象此过程。考虑这样的事情:
int averageAttribute(Animal[] animals, Func<Animal, int> getter)
{
double average = 0;
for (…)
average += getter(animals[i]);
return average/animals.length;
}
然后我们可以调用
averageAttribute(animals, (animal) => animal.age);
或类似的东西。但是,使用委托比必须的要慢;我们正在使用整个函数来 return Animal
结构中某个位置的值。在 C++ 中,指针成员允许您对结构进行指针数学运算(不是正确的术语,但我想不出更好的术语)。正如你所说
int p_fourthAnimal = 3;
(animals + p_fourthAnimal)*
要获取存储在变量 animals
中的指针前面这么多字节的值,在 C++ 中,您可以说
int Animal::* p_age = &Animal::age;
animal.*p_age //(animal + [the appropriate offset])*
获取变量中存储的指针前面这么多字节的值animal
;从概念上讲,编译器会将 animal.*p_age
转换为 (animal + [the appropriate offset])*
。因此,我们可以将 averageAttribute
声明为:
int averageAttribute(Animal[] animals, Animal::* member)
{
double average = 0;
for (…)
average += animals[i].*member; //(animals[i] + [offset])*
return average/animals.length;
}
然后我们可以调用
averageAttribute(animals, &Animal::age);
总之,指向成员的指针允许您将方法(例如我们的 averageAttribute
)抽象为结构的所有成员,而无需复制和粘贴代码。 虽然委托可以实现相同的功能,但如果您知道自己实际上并不需要函数分配给您的自由,那么这是一种相当低效的获取结构成员的方法,并且甚至可能存在委托不能满足要求的边缘用例,但我无法给出此类用例的任何示例。 C# 有类似的功能吗?
不,c# 没有像 c++ 那样指向(引用)对象成员的功能。
但是为什么呢?
指针被认为是不安全的。即使在不安全区域,您也不能指向引用或包含引用的结构,因为即使指针指向对象引用,它也可能被垃圾回收。垃圾收集器不跟踪对象是否被任何指针类型指向。
你提到了很多重复的代码是用非指针方式实现的,这是不正确的。
速度取决于JIT编译的好坏,你没测试过?
- 如果您真的 运行 陷入性能问题,您需要考虑您的数据结构,而不是某种访问成员的方式。
如果认为您的 Q 下的评论数量表明您没有真正解决 c# 普遍接受的缺点
var Animals = new Animal[100];
//fill array
var AvgAnimal = new Animal() {
age = (int)Animals.Average(a => a.age ),
height = (int)Animals.Average(a => a.height),
weight = (int)Animals.Average(a => a.weight)
};
c# 的 unsafe
区域提供 some ways 通过指针访问成员,但仅限于单个结构的值类型,而不是结构数组。
struct CoOrds
{
public int x;
public int y;
}
class AccessMembers
{
static void Main()
{
CoOrds home;
unsafe
{
CoOrds* p = &home;
p->x = 25;
p->y = 12;
System.Console.WriteLine("The coordinates are: x={0}, y={1}", p->x, p->y );
}
}
}
您可以使用委托获得相同的行为,但这与委托在 C++ 中是指向函数的指针不同。您尝试实现的目标在 C# 中是可能的,但不是您在 C++ 中的实现方式。
我考虑使用 Func
的解决方案:
public class Animal
{
public int Age { get; set; }
public int Height { get; set; }
public double Weight { get; set; }
public string Name { get; set; }
public static double AverageAttributeDelegates(List<Animal> animals, Func<Animal, int> getter)
{
double average = 0;
foreach(Animal animal in animals)
{
average += getter(animal);
}
return average/animals.Count;
}
}
List<Animal> animals = new List<Animal> { new Animal { Age = 1, Height = 2, Weight = 2.5, Name = "a" }, new Animal { Age = 3, Height = 1, Weight = 3.5, Name = "b" } };
Animal.AverageAttributeDelegates(animals, x => x.Age); //2
Animal.AverageAttributeDelegates(animals, x => x.Height); //1.5
它正在工作,但您被绑定到 属性 的 int
类型,因为 func 被声明为 Func<Animal, int>
。您可以设置为 object
并处理转换:
public static double AverageAttributeDelegates2(List<Animal> animals, Func<Animal, object> getter)
{
double average = 0;
foreach(Animal animal in animals)
{
int value = 0;
object rawValue = getter(animal);
try
{
//Handle the cast of the value
value = Convert.ToInt32(rawValue);
average += value;
}
catch(Exception)
{}
}
return average/animals.Count;
}
示例:
Animal.AverageAttributeDelegates2(animals, x => x.Height).Dump(); //1.5
Animal.AverageAttributeDelegates2(animals, x => x.Weight).Dump(); //3
Animal.AverageAttributeDelegates2(animals, x => x.Name).Dump(); //0
正如其他人在这里评论的那样,委托是在 C# 中实现这一目标的方式。
While a delegate can achieve the same functionality, it is a rather
inefficient way to get a member of a struct if you know you do not
actually need the freedom allotted to you by a function
这取决于编译器和运行时如何实现该委托。他们很可能会看到这是一个微不足道的函数并优化调用,就像他们对微不足道的 getter 和 setter 所做的那样。例如在 F# 中,您可以实现这一点:
type Animal = { Age : int }
let getAge (animal:Animal) =
animal.Age
let inline average (prop:Animal->int) (animals:Animal[]) =
let mutable avg = 0.
for animal in animals do
avg <- avg + float(prop(animal)) // no function call in the assembly here when calling averageAge
avg / (float(animals.Length))
let averageAge = average getAge
在 C++ 中,您可以编写以下代码:
int Animal::*pAge= &Animal::age;
Animal a;
a.*pAge = 50;
C# 中是否有类似的功能?
编辑: 澄清一下,我不是询问指针。我问的是 "pointers to members",C++ 中的一个特性,与 .*
和 ->*
运算符一起使用。
编辑 2:这是成员指向指针的用例示例。
假设我们有以下 class:
class Animal
{
int age;
int height;
int weight;
…
}
假设我们想要编写可以找到平均值的方法 age/height/weight/etc。数组中的所有动物。然后我们可以这样做:
int averageAge(Animal[] animals)
{
double average = 0;
for (…)
average += animals[i].age;
return average/animals.length;
}
int averageHeight(Animal[] animals)
{
//code here again
}
int averageWeight(Animal[] animals)
{
//code here again
}
我们最终会在此处复制和粘贴大量代码,如果我们寻找平均值的算法发生变化,我们将遇到维护噩梦。因此,我们希望为任何成员 抽象此过程。考虑这样的事情:
int averageAttribute(Animal[] animals, Func<Animal, int> getter)
{
double average = 0;
for (…)
average += getter(animals[i]);
return average/animals.length;
}
然后我们可以调用
averageAttribute(animals, (animal) => animal.age);
或类似的东西。但是,使用委托比必须的要慢;我们正在使用整个函数来 return Animal
结构中某个位置的值。在 C++ 中,指针成员允许您对结构进行指针数学运算(不是正确的术语,但我想不出更好的术语)。正如你所说
int p_fourthAnimal = 3;
(animals + p_fourthAnimal)*
要获取存储在变量 animals
中的指针前面这么多字节的值,在 C++ 中,您可以说
int Animal::* p_age = &Animal::age;
animal.*p_age //(animal + [the appropriate offset])*
获取变量中存储的指针前面这么多字节的值animal
;从概念上讲,编译器会将 animal.*p_age
转换为 (animal + [the appropriate offset])*
。因此,我们可以将 averageAttribute
声明为:
int averageAttribute(Animal[] animals, Animal::* member)
{
double average = 0;
for (…)
average += animals[i].*member; //(animals[i] + [offset])*
return average/animals.length;
}
然后我们可以调用
averageAttribute(animals, &Animal::age);
总之,指向成员的指针允许您将方法(例如我们的 averageAttribute
)抽象为结构的所有成员,而无需复制和粘贴代码。 虽然委托可以实现相同的功能,但如果您知道自己实际上并不需要函数分配给您的自由,那么这是一种相当低效的获取结构成员的方法,并且甚至可能存在委托不能满足要求的边缘用例,但我无法给出此类用例的任何示例。 C# 有类似的功能吗?
不,c# 没有像 c++ 那样指向(引用)对象成员的功能。
但是为什么呢? 指针被认为是不安全的。即使在不安全区域,您也不能指向引用或包含引用的结构,因为即使指针指向对象引用,它也可能被垃圾回收。垃圾收集器不跟踪对象是否被任何指针类型指向。
你提到了很多重复的代码是用非指针方式实现的,这是不正确的。
速度取决于JIT编译的好坏,你没测试过?
- 如果您真的 运行 陷入性能问题,您需要考虑您的数据结构,而不是某种访问成员的方式。
如果认为您的 Q 下的评论数量表明您没有真正解决 c# 普遍接受的缺点
var Animals = new Animal[100];
//fill array
var AvgAnimal = new Animal() {
age = (int)Animals.Average(a => a.age ),
height = (int)Animals.Average(a => a.height),
weight = (int)Animals.Average(a => a.weight)
};
c# 的 unsafe
区域提供 some ways 通过指针访问成员,但仅限于单个结构的值类型,而不是结构数组。
struct CoOrds
{
public int x;
public int y;
}
class AccessMembers
{
static void Main()
{
CoOrds home;
unsafe
{
CoOrds* p = &home;
p->x = 25;
p->y = 12;
System.Console.WriteLine("The coordinates are: x={0}, y={1}", p->x, p->y );
}
}
}
您可以使用委托获得相同的行为,但这与委托在 C++ 中是指向函数的指针不同。您尝试实现的目标在 C# 中是可能的,但不是您在 C++ 中的实现方式。
我考虑使用 Func
的解决方案:
public class Animal
{
public int Age { get; set; }
public int Height { get; set; }
public double Weight { get; set; }
public string Name { get; set; }
public static double AverageAttributeDelegates(List<Animal> animals, Func<Animal, int> getter)
{
double average = 0;
foreach(Animal animal in animals)
{
average += getter(animal);
}
return average/animals.Count;
}
}
List<Animal> animals = new List<Animal> { new Animal { Age = 1, Height = 2, Weight = 2.5, Name = "a" }, new Animal { Age = 3, Height = 1, Weight = 3.5, Name = "b" } };
Animal.AverageAttributeDelegates(animals, x => x.Age); //2
Animal.AverageAttributeDelegates(animals, x => x.Height); //1.5
它正在工作,但您被绑定到 属性 的 int
类型,因为 func 被声明为 Func<Animal, int>
。您可以设置为 object
并处理转换:
public static double AverageAttributeDelegates2(List<Animal> animals, Func<Animal, object> getter)
{
double average = 0;
foreach(Animal animal in animals)
{
int value = 0;
object rawValue = getter(animal);
try
{
//Handle the cast of the value
value = Convert.ToInt32(rawValue);
average += value;
}
catch(Exception)
{}
}
return average/animals.Count;
}
示例:
Animal.AverageAttributeDelegates2(animals, x => x.Height).Dump(); //1.5
Animal.AverageAttributeDelegates2(animals, x => x.Weight).Dump(); //3
Animal.AverageAttributeDelegates2(animals, x => x.Name).Dump(); //0
正如其他人在这里评论的那样,委托是在 C# 中实现这一目标的方式。
While a delegate can achieve the same functionality, it is a rather inefficient way to get a member of a struct if you know you do not actually need the freedom allotted to you by a function
这取决于编译器和运行时如何实现该委托。他们很可能会看到这是一个微不足道的函数并优化调用,就像他们对微不足道的 getter 和 setter 所做的那样。例如在 F# 中,您可以实现这一点:
type Animal = { Age : int }
let getAge (animal:Animal) =
animal.Age
let inline average (prop:Animal->int) (animals:Animal[]) =
let mutable avg = 0.
for animal in animals do
avg <- avg + float(prop(animal)) // no function call in the assembly here when calling averageAge
avg / (float(animals.Length))
let averageAge = average getAge