C# 4.0-многомерный ассоциативный массив (или способ имитации?)

Я опытный разработчик PHP, переходящий на C#. В настоящее время я работаю над приложением Windows Forms.

я обнаружил в своих поисках, что C# не поддерживает ассоциативные массивы так же свободно, как это делает PHP. Я нашел информацию о словаре и что-то о "структурах", которые кажутся объектами класса.

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

приложение читает текстовый файл журнала, ищет предопределенную строку, вытаскивает дату в этой строке, когда строка найдена, и увеличивает количество совпадений этой строки в эту дату.

в PHP это было бы так просто:

// Initialize
$count_array[$string_date][$string_keyword] = 0;

...

// if string is found
$count_array[$string_date][$string_keyword] += 1;

...

// To ouput contents of array
foreach($count_array as $date -> $keyword_count_array) {
    echo $date; // output date

    foreach($keyword_count_array as $keyword -> $count) {
        echo $keyword . ": " . $count;
    }
}

Кажется, это немного больше связано с C# (что не плохо). Я попытался использовать предложение, которое я нашел на другом подобном вопросе, но я действительно не следуйте за тем, как увеличить или повторить / вывести содержимое:

// Initialize
var count_array = new Dictionary<string, Dictionary<string, int>>();
count_array = null;

...

// if string is found - I think the second reference is supposed to be a Dictionary object??
count_array[string_date.ToShortDateString()][string_keyword]++;

...

// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in exportArray)
{
    foreach(KeyValuePair<string, int> kvp2 in kvp.Value) 
    {
        MessageBox.Show(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
    }
}

Я даже на правильном пути? Или у кого-то есть лучший/более чистый метод имитации PHP-кода выше?

обновление

с вышеуказанным кодом C# я фактически получаю ошибку в строке "/ / if string is found". Ошибка "ссылка на объект не указывает на экземпляр объекта". Я предполагаю, что это потому, что у меня есть строка в secound reference, а не объект Dictionary. Поэтому прямо сейчас я не уверен,как увеличить.

обновление 2

спасибо всем за ваше время. Текущий код теперь работает благодаря пониманию того, как работает словарь. Однако все советы относительно использования классов и объектов для этой ситуации также не теряются. Я могу рефакторинг масти.

6 ответов


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

перед вызовом

count_array[string_date.ToShortDateString()][string_keyword]++;

вам понадобится:

string shortDate = string_date.ToShortDateString();
if (!count_array.ContainsKey(shortDate))
{
    count_array.Add(shortDate, new Dictionary<string, int>());
}

if (!count_array[shortDate].ContainsKey(string_keyword))
{
    count_array[shortDate].Add(string_keyword, 0);
}

прежде чем вы попытаетесь увеличить что-либо.

вам нужно инициализировать свои словарные записи, позвонив .Добавить или [ключ] = значение. Вызов ++ для неинициализированной записи словаря не будет работать. В зависимости от того, что именно вы пытаетесь для выполнения, хотя это может быть хорошей идеей использовать класс.


Кортеж можно использовать для создания многомерного ключа для использования в словаре.

Dictionary<Tuple<TKey1,TKey2>,TValue>

или словарь словаря:

Dictionary<TKey1,Dictionart<TKey2,Tvalue>>

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

но, возможно, вы можете использовать некоторые linq, но ваш код немного неполный для этого.


Как насчет создания класса для этого?

public class LogEntry 
{
   private List<int> _lines = new List<int>();
   public string LogContent { get;set; }
   public DateTime Time { get;set; }
   public List<int> Lines { get { return _lines; } }
}

у вас все еще есть словарь, вероятно, DateTime, LogEntry? Не совсем уверен, что именно вам нужно / что ключ.

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


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

count_array[string_date.ToShortDateString()][string_keyword]++;

вы рассчитываете на count_array[string_date.ToShortDateString()] уже создан (так что его можно запросить). И еще одна проблема в том, что Dictionary<Key, Value> поведение заключается в том, что попытка доступа к элементу, который не существует результаты в исключении (KeyNotFoundException). Есть более снисходительный TryGetValue способ для вас, чтобы использовать. В сочетании, вам нужно сделать что-то вроде:

// Initialize
var count_array = new Dictionary<string, Dictionary<string, int>>();

// if string is found - I think the second reference is supposed to be a Dictionary object??
Dictionary<string, int> perDateDict;
var dateKey = string_date.ToShortDateString();
if (!count_array.TryGetValue(dateKey, out perDateDict)
{
    perDateDict = new Dictionary<string, int>();
    count_array.Add(adteKey, perDateDict);
}
int prevValue;
// note that when not found, prevValue will be zero, which is what we need
perDateDict.TryGetValue(string_keyword, out prevValue);
perDateDict[string_keyword] = prevValue+1;

// To ouput contents of "array"
foreach (KeyValuePair<string, Dictionary<string, int>> kvp in exportArray)
{
    foreach(KeyValuePair<string, int> kvp2 in kvp.Value) 
    {
        MessageBox.Show(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
    }
}

одна вещь, вы должны быть уверены, что словарь не является ValueType и не инициализируется автоматически.

следовательно, когда вы говорите count_array = null, это означает, что вы сбрасываете ссылку на нулевое местоположение. Просто удалите линию.

ваш код должен выглядеть так :

    var count_array = new Dictionary<string, Dictionary<string, int>>();

    // if string is found - I think the second reference is supposed to be a Dictionary object??
    string dt = DateTime.Now.ToShortDateString();
    count_array[dt] = new Dictionary<string, int>(); //It is important as you should always give appropriate refernece before doing a fetch.

    count_array[dt]["key"] = 0; //Value types are defaults to 0 so it is not explicitely required.

    //Now you can do 
    count_array[dt]["key"]++;

    // To ouput contents of "array"
    foreach (KeyValuePair<string, Dictionary<string, int>> kvp in count_array)
    {
        foreach (KeyValuePair<string, int> kvp2 in kvp.Value)
        {
            Console.WriteLine(kvp.Key + " - " + kvp2.Key + " = " + kvp2.Value);
        }
    }

вы также можете использовать ?? оператор, чтобы убедиться, что когда словарь равен null, вы назначаете новую ссылку.

count_array[dt] = count_array[dt]?? новый Dictionary();

Я надеюсь, что это поможет вам даже вы должны перекодировать правильно это.


вам нужно начать думать в терминах OO. В производственном коде я бы дал классам какое-то назначение для печати, вместо того, чтобы идти непосредственно к консоли, и, возможно, использовать стратегию или аналогичный формат текста, но, по сути, это способ мышления ОО о проблеме.

class Log {
    Dictionary<DateTime, List<LogEntry>} Entries { get; private set; }

    public void PrintLogs()
    {
        foreach (var date in Entries.Keys)
        {
            Console.WriteLine(date);

            foreach (var entry in Entries[date])
            {
                entry.PrintEntry();
            }
        }
    }
}

class LogEntry {
    public List<string> EntryLines { get; set; }
    public DateTime Date { get; set; }

    public void PrintEntry()
    {
        foreach (var line in EntryLines)
            Console.WriteLine(line);
        }
}