简体   繁体   中英

Can someone please explain this delegate syntax?

In a class library called DataAccess I find the following declaration and usage:

public static class DataAccess
{
    public delegate T LoadObject<T>(SqlDataReader dataReader);

    public static Dictionary<TKey, TValue> GetDictionaryFromReader<TKey, TValue>(
        Database database
        , DbCommand dbCommand
        , LoadObject<KeyValuePair<TKey, TValue>> loadMethod
        )
    {
        Dictionary<TKey, TValue> _dictionary = new Dictionary<TKey, TValue>();

        using (SqlDataReader dataReader = StoredProcedures.ExecuteSqlDataReader(database, dbCommand))
        {
            GenerateDictionary<TKey, TValue>(dataReader, ref _dictionary, loadMethod);
        }

        return _dictionary;
    }
}

GetDictionaryFromReader above is called by this static method:

public static Dictionary<String, String> GetGroupTypesList()
{
    Dictionary<string, string> dict = new Dictionary<string, string>();

    Database database = CenestDatabaseFactory.CreateDatabase();

    DbCommand dbCommand = database.GetStoredProcCommand(SP_LIST_GROUP_TYPES);

    dict = DataAccess.GetDictionaryFromReader<string, string>(database, dbCommand, _loadGroupType);

    return dict;
}

It passes "_loadGroupType" into "loadMethod". "_loadGroupType" looks like this:

private static KeyValuePair<string, string> _loadGroupType(SqlDataReader returnData)
{
    KeyValuePair<string, string> entry =
        new KeyValuePair<string, string>((string)returnData["Group_Type"], (string)returnData["Group_Type_Desc"]);

    return entry;
}

I sort of "get" that the _loadGroupType method is being passed as a parameter into GetDictionaryFromReader. Okay, but why? And what is this delegate declaration syntax supposed to say?

public delegate T LoadObject<T>(SqlDataReader dataReader);

I'd really like to understand what is going on with this. You don't need to explain it yourself -- but can you point to something that will be able to make it clear to me?

Thanks!

Additional Note to @ReedCopsey:

So...

In this using statement:

using (SqlDataReader dataReader = StoredProcedures.ExecuteSqlDataReader(database, dbCommand))
{
    GenerateDictionary<TKey, TValue>(dataReader, ref _dictionary, loadMethod);
}

Is C# smart enough to recognize that loadMethod needs the SqlDataReader passed to it, and does so -- because of the using statement and the initial delegate declaration? Because otherwise I can't see how the data reader makes it into the method.

Of course not! As per @ReedCopsey the GenerateDictionary method uses the SqlDataReader, Duh:

private static void GenerateDictionary<TKey, TValue>(
    SqlDataReader dataReader
    , ref Dictionary<TKey, TValue> dictionary
    , LoadObject<KeyValuePair<TKey, TValue>> loadMethod
    )
{
    while (dataReader.Read())
    {
        KeyValuePair<TKey, TValue> kvp = loadMethod(dataReader);
        dictionary.Add(kvp.Key, kvp.Value);
    }
}

And what is this delegate declaration syntax supposed to say?

That is a delegate which is typed to be a method that accepts a SqlDataReader as an input, and returns a generic type T as the result.

In your case, the T result is KeyValuePair<string,string> .


Is C# smart enough to recognize that loadMethod needs the SqlDataReader passed to it, and does so -- because of the using statement and the initial delegate declaration? Because otherwise I can't see how the data reader makes it into the method.

No. The GenerateDictionary<TKey,TValue> method is going to use the dataReader passed into it to call the delegate.

The declaration public delegate T LoadObject<T>(SqlDataReader dataReader) is expecting a SqlDataReader and will return whatever type you want it to. Could be an int , a List<string> or even another delegate. That's because there's no generic constraint on the type T and it is only associated with the return type of the delegate.

On the other hand, we have private static KeyValuePair<string, string> _loadGroupType(SqlDataReader returnData) . This is a function that receives a SqlDataReader and returns a... wait, it doesn't matter, could be anything! So the _loadGroupType does "match" against the delegate LoadObject<T> with T = KeyValuePair<string, string> and can be implicitly used as such.

If there was a delegate declared such as delegate KeyValuePair<string, string> D2(SqlDataReader reader); it would also match, but without the generics this time. When the type doesn't matter for an operation (or it can be contrained), generics are your friend.

First the declaration says: LoadObject will be a method that takes an SqlDataReader and returns a T , where T is defined by the user.

Then GetDictionaryFromReader says it needs a LoadObject<KeyValuePair<TKey, TValue>> , so T is KeyValuePair<TKey, TValue> here, so it needs a method takes an SqlDataReader and returns KeyValuePair<TKey, TValue> .

At last, when you call GetDictionaryFromReader , passing _loadGroupType , it takes an SqlDataReader and returns KeyValuePair<TKey, TValue> , just fits what GetDictionaryFromReader want.

I don't think there is anything diffcult to understand.

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