Использование словарей для присвоения значений переменным - PullRequest
0 голосов
/ 15 апреля 2020

У меня есть следующий код, который, в качестве примера, предназначен для того, чтобы взять fruitName и присвоить его следующей доступной неиспользуемой строковой переменной name # (той, которой еще не присвоено имя плода).

Желая избежать использования вложенных операторов IF, я пытаюсь использовать словарь следующим образом.

public static void AssignFruitToNextAvailableSlot(string fruitName)
        {
            string NextEmptyNameSlot = "";

            string Name1 = "Apple";
            string Name2 = "Orange";
            string Name3 = "Tomato";
            string Name4 = "";
            string Name5 = "";
            string Name6 = "";
            string Name7 = "";
            string Name8 = "";
            string Name9 = "";


            Dictionary<string, string> nameSlots = new Dictionary<string, string>()
                {
                    {"Slot1", Name1},
                    {"Slot2", Name2},
                    {"Slot3", Name3},
                    {"Slot4", Name4},
                    {"Slot5", Name5},
                    {"Slot6", Name6},
                    {"Slot7", Name7},
                    {"Slot8", Name8},
                    {"Slot9", Name9}
                };

            foreach (KeyValuePair<string, string> nameSlot in nameSlots)
            {
                NextEmptyNameSlot = nameSlot.Key;

                if (nameSlot.Value == "")
                {
                    break;
                }
            }
            Console.WriteLine($"Available name slot at {NextEmptyNameSlot}");
            Console.WriteLine($"Content of empty name slot \"{nameSlots[NextEmptyNameSlot]}\"");
            nameSlots[NextEmptyNameSlot] = fruitName;
            Console.WriteLine($"Empty image slot has been assigned the value {nameSlots[NextEmptyNameSlot]}");
            Console.WriteLine($"Empty image slot has been assigned the value {Name4}");
            Console.ReadLine();
        }

Пример вывода для AssignFruitToNextAvailableSlot ("Strawberry"):

  • Доступный слот имени в Slot4
  • Содержимое пустого слота имени ""
  • Пустому слоту изображения присвоено значение Strawberry
  • Пустому слоту изображения присвоено значение

Как видите, код отлично работает для идентификации пустого слота имени, в данном случае Slot4. Однако при использовании синтаксиса ...

nameSlots[NextEmptyNameSlot] = fruitName

... "клубника" присваивается nameSlots [NextEmptyNameSlot], но не переменной Name4. Я попытался использовать «ref» для назначения по ссылке, но это привело к различным сообщениям об ошибках.

** Какой правильный синтаксис назначает fruitName "Strawberry" для строковой переменной Name4 с помощью словаря? Извините, если это очень простой вопрос. Я новичок в C#. **

Ответы [ 3 ]

1 голос
/ 15 апреля 2020

Хотя это не является основной проблемой, запутанным фактором является то, что строки являются неизменяемыми, что означает, что если вы «измените», то они будут выброшены, и будет создана новая. Я вернусь к этому моменту через мгновение

Основная проблема заключается в том, что вы не можете установить sh ссылку на что-то:

string s = "Hello";

Затем установите sh другое ссылка на него (как вы делаете со словарем)

string t = s;

Затем «измените оригинальную вещь», изменив эту новую ссылку на что-то другое:

t = "Good bye";

И надеюсь, что оригинал s изменился. s по-прежнему указывает на строку с надписью "Hello". t раньше также указывал на «Hello» (он никогда не указывал на s, указывающий на привет в какой-то цепочке), но теперь указывает на новую строку «Good bye». Мы никогда не использовали ключевое слово new, когда говорили «До свидания», но компилятор должен был использовать его, и new создал другой объект и изменил ссылку на него. Поскольку ссылки не связаны друг с другом, вы не можете изменить то, на что указывает нижестоящая переменная, и надеетесь, что переменная обратного потока, указывающая на то же самое, также изменится.

//we had this
s ===> "Hello" <=== t

//not this
t ==> s ==> "Hello"

//then we had this
s ===> "Hello"      t ===> "Good bye"

Поскольку у нас есть две независимые ссылки, которые указывают на одну и ту же вещь, единственный способ, которым вы можете работать с одной ссылкой и видеть другую, - это изменение содержимого того, на что они указывают, что означает вам придется использовать что-то изменчивое, а не выбрасывать. Вот где строка путает вещи, потому что строки не могут быть изменены после создания. Они ДОЛЖНЫ быть выброшены и сделаны новые. Вы не видите new - компилятор сделает это за вас.

Таким образом, вместо использования строк, мы должны использовать что-то вроде строки, но ее содержимое может быть изменено:

StringBuilder sb1 = new StringBuilder("Hello");
StringBuilder sb2 = null;

var d = new Dictionary<string, StringBuilder>();

d["sb1"] = sb1;
d["sb2"] = sb2;

Теперь вы можете изменить строку в изменяемой переменной sb1, доступ к string Builder через словарь:

d["sb1"].Clear();
d["sb1"].Append("Good bye");

Console.Write(sb1.Length); //prints 8, because it contains: Good bye

Но вы все равно не можете назначить новые ссылки на переменные sb, используя словарь:

d["sb2"] = new StringBuilder("Oh");

//sb2 is still and will always be null
Console.Write(sb2.Length); //null reference exception

Использование new останавливает словарь, указывающий на sb2 и указывает на что-то еще, sb2 не изменяется и все еще является нулевым. Нет практического способа установить sb2 для экземпляра stringbuilder, используя словарь d

. Именно поэтому оригинальная строковая вещь не сработала - вы не можете изменить содержимое строка - c# выбросит старую строку и сделает new один и каждый раз, когда new используется любая ссылка, которая могла бы указывать на старую вещь, теперь будет указывать на новую вещь

В результате вам придется инициализировать все ваши ссылки на что-то заполненное или пустое:

var sb1 = new StringBuilder("Hello");
var sb2 = new StringBuilder("Goodbye");
var sb3 = new StringBuilder("");
var sb4 = new StringBuilder("");

Вам нужно будет связать свой словарь со всеми из них:

d["sb1"] = sb1;
d["sb2"] = sb2;
d["sb3"] = sb3;
d["sb4"] = sb4;

И вам придется пропустить свой словарь в поисках пустого:

//find empty one
for(int i = 1, i <= 4; i++){
  if(d["sb"+i].Length ==0)
    return "sb"+i;
}

, а затем изменить его содержимое


Это все сложно, и я полностью согласен с ответом дано Джейсоном, который говорит вам использовать массивы (потому что это то, для чего они были изобретены), но я надеюсь, что я ответил на ваши вопросы о том, почему C# не работает так, как вы ожидали

1 голос
/ 15 апреля 2020

Я думаю, вы делаете это более сложным, чем нужно. Если у вас фиксированное количество элементов и вы хотите, чтобы порядок оставался неизменным, то массив - это самый простой способ обработки данных.

Объявите массив с 9 элементами.

string[] fruitArray = new string[9];

Затем сохраните счетчик количества используемых вами слотов;

int NextSlot = 0;

Увеличивайте его после добавления фрукта

NextSlot++;

Затем вы можете повторять это для отображения данных

for(int loop =0; loop <= fruitArray.Length; loop++)
{
   Console.WriteLine($"Slot {loop + 1} contains the value {fruitArray[loop]}");
}

Если у вас нет фиксированного числа элементов или вы не знаете размер во время разработки, тогда вы можете использовать List<string> вместо массива.

Список будет сохранять порядок вставки данных, пока вы не отсортируете их.

0 голосов
/ 15 апреля 2020

Если такая странная вещь абсолютно необходима, вы можете использовать Reflection.

var frutSlots = this.GetProperties()
.Where(p => p.Name.StartsWith("Name")).OrderBy(p => p.Name).ToList();

Вы получите список PropertyInfo объектов, упорядоченных по имени свойства, через который вы можно повторять или просто использовать Linq.

fruitSolts.First(fs => fs.GetValue(this).ToString()="").SetValue(this."somefruit");

Но учтите, что отражения не слишком быстрые и не слишком хорошие для производительности.

...