C# 4.0 - 多维关联数组(或者模仿数组的方法?)

发布于 2024-10-19 06:55:13 字数 1667 浏览 1 评论 0原文

我是一名经验丰富的 PHP 开发人员,正在转向 C#。目前我正在开发一个 Windows 窗体应用程序。

我在搜索中发现 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”行收到错误。错误是“对象引用未设置为对象的实例”。我假设这是因为我在第二个引用中有一个字符串,而不是一个 Dictionary 对象。所以现在我不确定如何增加。

更新2

感谢大家的宝贵时间。由于了解了 Dictionary 的工作原理,当前代码现在可以正常运行了。然而,关于在这种情况下使用类和对象的所有建议也不会丢失。我可能会重构以适应。

I'm an experienced PHP developer transitioning to C#. At present I am working on a Windows Forms application.

I found in my searches that C# doesn't support associative arrays in the same loose fashion PHP does. I have found info on Dictionary and something about "structs" which seem to be class objects.

The trouble I am having is getting my head around not only an Associative array, but a multi dimensional one that I want to use for keeping multiple counts in a series of loops.

The application is reading a text log file, searching for a predefined string, pulling out the date on that line when the string is found, and incrementing a count for that string match on that date.

In PHP, it would be as easy as this:

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

It seems to be a little more involved in C# (which isn't a bad thing). I have tried using an suggestion I found on another similar question but I don't really follow how to either increment or iterate/output the contents:

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

Am I even on the right track? Or does someone have a better/cleaner method of mimicing the PHP code above?

UPDATE

With the above C# code, I actually get an error at the "// if string is found " line. The error is "Object reference is not set to an instance of an object". I am assuming that it is because I have a string in the secound reference, not a Dictionary object. So right now, I am unsure how to increment.

UPDATE 2

Thanks everyone for your time. Current code is now functional thanks to understanding how Dictionary's work. However all advice regarding the use of classes and objects for this situation is not lost either. I may refactor to suit.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

东北女汉子 2024-10-26 06:55:13

代码本身看起来不错,我认为唯一缺少的是在增加值之前没有检查这些值是否存在。

在致电之前

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

在尝试增加任何内容之前。

您需要通过调用 .Add 或 ["key"] = value 来初始化字典条目。对未初始化的字典条目调用 ++ 不起作用。尽管使用类可能是一个好主意,但这取决于您想要完成的具体任务。

The code itself looks sound, the only thing I see missing is there are no checks to see if the values exist before incrementing them.

Before you call

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

You'll need to do:

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

Before you try incrementing anything.

You need to initialize your dictionary entries by calling .Add or ["key"] = value. Calling ++ on an uninitialized dictionary entry won't work. Depending on what exactly it is you're trying to accomplish though it might be a good idea to use a class.

滥情空心 2024-10-26 06:55:13

您可以使用元组创建多维键以在字典中使用。

Dictionary<Tuple<TKey1,TKey2>,TValue>

或者字典的字典:

Dictionary<TKey1,Dictionart<TKey2,Tvalue>>

第二个使用起来更烦人,但它的优点是您可以仅使用第一个键对其进行索引,然后获取与该键关联的所有键值对。

但也许您可以使用一些 linq,但您的代码对此有点不完整。

You can use a tuple to create a multi-dimensional key for use in a Dictionary.

Dictionary<Tuple<TKey1,TKey2>,TValue>

Or a Dictionary of Dictionary:

Dictionary<TKey1,Dictionart<TKey2,Tvalue>>

The second one is more annoying to work with, but has the upside that you can index into it with just the first key and then get all key-value pairs associated with that key.

But perhaps you can use some linq, but your code is a bit incomplete for that.

半窗疏影 2024-10-26 06:55:13

为此创建一个类怎么样?

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 的字典吗?不完全确定您到底需要什么/关键是什么。

无论如何,创建一个类似乎是“正确”的方式,因为你可以更清楚地表达你的意图。

What about creating a class for this?

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

You'd still have a dictionary of probably DateTime, LogEntry? Not entirely sure what exactly you need / what the key is.

Anyways, creating a class seems to be the "correct" way as you can express your intend more clearly.

谁把谁当真 2024-10-26 06:55:13

您的方法可行,但是,您需要了解 Dictionary 是一种引用类型,这意味着它必须在使用之前创建。您创建“顶级”词典,但也需要创建“二级”词典。但是

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

您指望 count_array[string_date.ToShortDateString()] 已经创建(以便可以查询)。另一个问题是 Dictionary 行为是尝试访问不存在的项目会导致异常 (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);
    }
}

Your approach can work, however, you need to understand Dictionary is a reference type, which means it has to be created prior to use. You create the “top-level” Dictionary, but the “second-level” dictionaries need to be created as well. But in

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

you count on count_array[string_date.ToShortDateString()] being already created (so that it can be queried). And another problem is that Dictionary<Key, Value> behavior is that an attempt to access an item which does not exist results in an exception (KeyNotFoundException). There is a more lenient TryGetValue method for you to use. Combined, you need to do something along the lines of:

// 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);
    }
}
ま柒月 2024-10-26 06:55:13

您必须确保 Dictionary 不是 ValueType 并且不会自动初始化的一件事。

因此,当您说 count_array = null 时,这意味着您正在将引用重置为 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);
        }
    }

您还可以使用 ??运算符以确保当 Dictionary 为 null 时,您分配一个新引用。

计数数组[dt] = 计数数组[dt] ??新字典();

我希望这会对您有所帮助,即使您应该正确地重新编码。

One thing you must be make sure Dictionary is not a ValueType and is not auto initialized.

Hence when you say count_array = null it means you are resetting the reference to null location. Just remove the line.

Your code should look like :

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

You can also use ?? operator to ensure that when Dictionary is null, you assign a new reference.

count_array[dt] = count_array[dt] ?? new Dictionary();

I hope this will help you even you should recode this properly.

木格 2024-10-26 06:55:13

您需要开始用面向对象的术语进行思考。在生产代码中,我会给类一些打印目的地,而不是直接进入控制台,并且可能使用策略或类似的方法来格式化文本,但本质上这是思考问题的 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);
        }
}

You need to start thinking in OO terms. In production code I would give the classes some destination to print to, instead of going directly to Console, and maybe use strategy or similar to format the text, but essentially this is the OO way of thinking about the problem.

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);
        }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文