Передача по ссылке, члена объекта в универсальной коллекции, к которой обращается индексатор - PullRequest
1 голос
/ 26 января 2012

Я создал экземпляр универсальной коллекции в C #, и мне нужно передать один из членов структуры в этой коллекции со ссылкой на метод.Могу ли я использовать индексатор универсальной коллекции, чтобы выбрать, какой элемент объекта я хочу изменить в методе?Кажется, я получаю сообщение об ошибке («Невозможно изменить возвращаемое значение выражения, потому что оно не является переменной»), но то, что я имею, похоже на это:использует std :: deque и глобальные методы, и я хочу сохранить как можно больше с точки зрения синтаксиса.Кто-нибудь знает элегантное решение этой проблемы?

Ответы [ 4 ]

2 голосов
/ 26 января 2012

Передача параметров с ключевым словом ref не рекомендуется в C #. Вместо этого, предполагая, что Card является классом, вы можете изменить сигнатуру метода, чтобы просто передать карту:

ModifyRank(deck_of_cards[4], 8); 

Поскольку классы ссылочных типов в C #, ModifyRank изменит содержимое карты, переданной в качестве параметра.

2 голосов
/ 26 января 2012

Невозможно передать по ссылке поле структуры, полученной через индексатор.Для изменения содержимого структуры в такой коллекции требуются отдельные операции get и set, поскольку индексатор не может вернуть значение по ссылке (по крайней мере, не в C # - это может быть возможно с непроверяемым MSIL).

Наиболее простым решением конкретной проблемы было бы:

Deque<Card> deck_of_cards = new Deque<Card>();  // standard deck of 52 playing cards
var tmp = deck_of_cards[4];
ModifyRank( ref tmp.rank, 8);  // Changes the rank (int) of the 5th card to 8
deck_of_cards[4] = tmp;
0 голосов
/ 26 января 2012

Хотя универсальные коллекции .net не обеспечивают хорошей поддержки для работы с изменяемыми структурами, если вы разрабатываете свои собственные коллекции, вы можете заставить их эффективно поддерживать изменяемые структуры. Я хотел бы предложить что-то вроде следующего шаблона (если предположить, что объект похож на EnhancedList<T>:

// Delegate type definitions--should go somewhere
  delegate ActionByRef<T1>(ref T1 p1);
  delegate ActionByRef<T1,T2>(ref T1 p1, ref T2 p2);
  delegate ActionByRef<T1,T2,T3>(ref T1 p1, ref T2 p2, ref T3 p3);
// Code within the EnhancedList<T> type:
  T _items[];  // Array to hold actual items
  void ActOnItem(int index, ActionByRef<T&gt proc)
    { proc(ref _items[index]); }
  void ActOnItem<PT1>(int index, ActionByRef<T,PT1&gt proc, ref PT1 p1)
    { proc(ref _items[index], p1); }
  void ActOnItem<PT1,PT2>(int index, ActionByRef<T,PT1,PT2&gt proc, 
                          ref PT1 p1, ref PT2 p2)
    { proc(ref _items[index], ref p1, ref p2); }

Используя такой подход, можно передать элементы коллекции по ссылке на любой нужный код. Немного раздражает, что нет никакого способа обработать универсальные переменные, но этот подход допускает произвольную истинную семантику передачи по ссылке при условии, что один из вариантов ActOnItem определен с достаточным количеством параметров.

0 голосов
/ 26 января 2012

Не видя класса Card, я не могу быть уверен, но я подозреваю, что свойство rank для Card - это свойство, а не поле.Другими словами, у вас есть объявление

public class Card
{
    public int rank { get; set; }
}

, а не

public class Card
{
    public int rank;
}

Но синтаксис свойства (мой первый пример) на самом деле является синтаксическим сахаром, который переписывается компилятором в нечто вроде

public class Card
{
    private int _rank;

    public int get_rank()
    {
        return rank;
    }

    public int set_rank(int rank)
    {
        _rank = rank;
    }
}

Это означает, что свойство rank в Card вовсе не int, а обертка вокруг двух методов.Поскольку rank не является int, вы не можете передать его по ссылке.

А теперь элегантное решение:

Если метод ModifyRank устанавливает только значениес его параметром ref вы можете упростить вторую строку кода до

deck_of_cards[4].rank = 8;

, что намного понятнее любому читателю.И если ваш ModifyRank метод делает что-то особенное, вы можете добавить этот метод расширения к некоторому статическому классу:

public static void ModifyRank(this Card c, int newRank)
{
    int rank = c.rank;
    ModifyRank(ref rank, newRank);
    c.rank = rank;
}

Это позволит вам заменить вторую строку на

deck[4].ModifyRank(8);
...