简体   繁体   中英

What is good practice when writing a method that modifies a c# collection

I'm refactoring some code and I have written a method that modifies a Dictionary and returns it. Is this a better practice than using an out parameter? I don't really want to create an extension method in this case because it would add the method to the Dictionary class which is overkill for what this is used for. Please don't point out that I shouldn't be using dynamic sql, that is another stage in the refactoring that currently has to be deferred.

private static Dictionary<int, string>
            FindMatches(Dictionary<int, string> records,
                        string queryFormat,
                        string region,
                        string type,
                        string label)
{
    var query = string.Format(queryFormat, SqlSvrName, SqlDbName, SqlSchemaName,
                                                             region, type, label);
    using (var dr = DataRepository.Provider.ExecuteReader(CommandType.Text, query))
    {
        if (dr != null && !dr.IsClosed)
        {
            while (dr.Read())
            {
                var assetID = (int)dr.GetDouble(0);
                if (!records.ContainsKey(assetID))
                    records[assetID] = dr.GetString(1);
            }
        }
    }
    return records;
}

Edit: I was a bit hasty with my use of the term out above. I'm trying to make it explicit in my code that the dictionary is modified by the method. An out parameter here would only make sense if the method created a new dictionary and returned it via that parameter. A little more context for this is that the method is called multiple times with different query strings and the dictionary may already contain matches.

Edit2: Just to follow up I removed the records parameter and instead return a list of KeyValuePair from FindMatches . I end up with a List<KeyValuePair<int, string>> which I convert to a dictionary via:

records
    .GroupBy(rec => rec.Key)
    .ToDictionary(grp => grp.Key, grp => grp.First().Value);

Why should your method modify an existing dictionary at all? It doesn't seem to use the existing keys/values, so make this method just return a new Dictionary<string, int> :

private static Dictionary<int, string>
            FindMatches(string queryFormat,
                        string region,
                        string type,
                        string label)
{
    var records = new Dictionary<int, string>();
    var query = string.Format(queryFormat, SqlSvrName, SqlDbName,
                              SqlSchemaName, region, type, label);
    using (var dr = DataRepository.Provider.ExecuteReader(CommandType.Text,
                                                          query))
    {
        if (dr != null && !dr.IsClosed)
        {
            while (dr.Read())
            {
                var assetID = (int)dr.GetDouble(0);
                // If each assetID in the database will be distinct, you 
                // don't need the "if" here, because you know the dictionary
                // is empty to start with
                if (!records.ContainsKey(assetID))
                {
                    records[assetID] = dr.GetString(1);
                }
            }
        }
    }
    return records;
}

You can then also write a separate method to merge two dictionaries in a particular way - or return a new dictionary which is the result of merging two existing ones.. Separate the two concerns.

Since you aren't actually using the Dictionary in the method (other than filling it), I would remove it from the input parameters and just return it instead.

If you have to pass the dictionary in as a parameter for some reason I would have a void return and rename the method to make sure it is obvious that it has side-effects to the input parameters - eg

void PopulateRecordsWithMatches(Dictionary<int, string> records...)

I believe that references to all complex objects in C# are passed by reference. So if you modify the dictionary via its reference inside a method, you don't need to return it or specify the out/ref keyword. It'll be changed outside the method's scope.

An out parameter would mean you pass in an empty dictionary. That wouldn't make sense. You probably mean a ref parameter.

But as Kon already said, you can modify the content of an object inside a method.

Since you're modifying the dictionary in place, I think it's best to make that explicit by taking it as a ref parameter:

private static void
            FindMatches(ref Dictionary<int, string> records,
                        string queryFormat,
                        string region,
                        string type,
                        string label)
{
    var query = string.Format(queryFormat, SqlSvrName, SqlDbName, SqlSchemaName,
                                                             region, type, label);
    using (var dr = DataRepository.Provider.ExecuteReader(CommandType.Text, query))
    {
        if (dr != null && !dr.IsClosed)
        {
            while (dr.Read())
            {
                var assetID = (int)dr.GetDouble(0);
                if (!records.ContainsKey(assetID))
                    records[assetID] = dr.GetString(1);
            }
        }
    }
}

Returning it or using it as an out parameter both imply that you're creating a new dictionary, which is not the case. Explicitly making it a ref parameter, on the other hand, is a clear indication that you intend to modify the dictionary as a side effect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM