NetherGranite
NetherGranite

Reputation: 2100

Does C# have pointers to members like in C++?

In C++, you could write the following code:

int Animal::*pAge= &Animal::age;

Animal a;

a.*pAge = 50;

Is there similar functionality in C#?

Edit: To clarify, I am not asking about pointers. I am asking about "pointers to members", a feature found in C++ that is used with the .* and ->* operators.


Edit 2: Here is an example of a use case for members to pointers.

Let's say we have the following class:

class Animal
{
    int age;
    int height;
    int weight;
    …
}

And let's say that we want to write methods that will find the average age/height/weight/etc. of all Animals in an array. We could then do this:

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
}

We would end up copying and pasting a lot of code here, and if our algorithm for finding the average changed, we would encounter a maintenance nightmare. Thus, we want an abstraction of this process for any member. Consider something like this:

int averageAttribute(Animal[] animals, Func<Animal, int> getter)
{
    double average = 0;

    for (…)
        average += getter(animals[i]);

    return average/animals.length;
}

which we could then call with

averageAttribute(animals, (animal) => animal.age);

or something similar. However, using delegates is slower than it has to be; we are using an entire function just to return the value at a certain location in the Animal struct. In C++, members to pointers allow you to do pointer math (not the right term but I can't think of a better term) on structs. Just as you can say

int p_fourthAnimal = 3;

(animals + p_fourthAnimal)*

to get the value so many bytes ahead of the pointer stored in the variable animals, in C++, you could say

int Animal::* p_age = &Animal::age;

animal.*p_age //(animal + [the appropriate offset])*

to get the value so many bytes ahead of the pointer stored in the variable animal; conceptually, the compiler will turn animal.*p_age into (animal + [the appropriate offset])*. Thus, we could declare our averageAttribute as this instead:

int averageAttribute(Animal[] animals, Animal::* member)
{
    double average = 0;

    for (…)
        average += animals[i].*member; //(animals[i] + [offset])*

    return average/animals.length;
}

which we could then call with

averageAttribute(animals, &Animal::age);

In summary, pointers to members allow you to abstract a method such as our averageAttribute to all members of a struct without having to copy and paste code. 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, and there could even be edge use cases in which a delegate does not suffice, but I could not give any examples of such use cases. Does C# have similar functionality?

Upvotes: 2

Views: 661

Answers (3)

Asik
Asik

Reputation: 22133

As other people have commented here, delegates are the way to achieve this in 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

It depends how the compiler and runtime implement that delegate. They could very well see that this is a trivial function and optimize the call away, like they do for trivial getters and setters. In F# for instance you can achieve this:

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

Upvotes: 3

Falco Alexander
Falco Alexander

Reputation: 3332

no, c# doesn't have a feature to point into (reference) object's members the way c++ does.

but why? A pointer is considered unsafe. And even in unsafe area you cannot point to a reference or to a struct that contains references, because an object reference can be garbage collected even if a pointer is pointing to it. The garbage collector does not keep track of whether an object is being pointed to by any pointer types.

  • you mentioned a lot of duplicate code is used to implement it the non-pointer way, which isn't true.

  • Speed depends on how well the JIT compiles it, but you didn't test?

  • if you really run into performance problems, you need to think about your data structures and less about a certain way to access members.

If think the amount of comments under your Q shows, that you did not really hit a commonly accepted drawback of 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)
};

the unsafe area of c# serves some ways access members by pointer, but only to value types like single structs and not for an array of structs.

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 );
    }
  }
}

Upvotes: 0

Phate01
Phate01

Reputation: 1795

You can get the same behaviour using delegates but that's not the same thing as delegates are pointers to functions in C++. What you're trying to achieve is possible in C# but not in the way you're doing in C++.

I think about a solution using 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

It's working but you are bound to the int type of the property since the func is declared as Func<Animal, int>. You could set to object and handle the cast:

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;
}

Example:

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

Upvotes: 1

Related Questions