Есть ли в C # указатели на элементы, как в C ++? - PullRequest
0 голосов
/ 01 февраля 2019

В C ++ вы могли бы написать следующий код:

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

Animal a;

a.*pAge = 50;

Есть ли подобная функциональность в C #?

Редактировать: Чтобы уточнить, я не спрашивать об указателях.Я спрашиваю об «указателях на членов», функция, найденная в C ++, которая используется с операторами .* и ->*.


Edit 2: ВотПример варианта использования членов для указателей.

Допустим, у нас есть следующий класс:

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

И, скажем, мы хотим написать методы, которые найдут средний возраст / рост/ вес / и т.д..всех животных в массиве.Тогда мы могли бы сделать это:

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

или что-то подобное.Однако с использованием делегатов медленнее, чем должно быть ;мы используем целую функцию только для того, чтобы вернуть значение в определенном месте в структуре 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 # похожую функциональность?

Ответы [ 3 ]

0 голосов
/ 01 февраля 2019

Вы можете получить такое же поведение, используя делегаты, но это не то же самое, что делегаты являются указателями на функции в 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<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
0 голосов
/ 01 февраля 2019

Как прокомментировали другие люди, делегаты - это способ достичь этого в C #.

Хотя делегат может достичь той же функциональности, это довольно неэффективный способ получить членЕсли вы знаете, что вам на самом деле не нужна свобода, предоставляемая вам функцией

Это зависит от того, как компилятор и среда выполнения реализуют этот делегат.Они могли очень хорошо видеть, что это тривиальная функция, и оптимизировать вызов, как они делают для тривиальных геттеров и сеттеров.Например, в 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
0 голосов
/ 01 февраля 2019

нет, у 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)
};

область unsafe в c # обслуживает некоторые способы доступа к членам по указателю, но только к типам значений, таким как отдельные структуры, а не для массива структур.

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 );
    }
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...