简体   繁体   中英

Mapping a single-method-to-multiple-strings Dictionary to a single-string-to-multiple-method Dictionary

I Dare say that's the most complicated title I've ever come up with. I'm just not sure how to better explain it, short of this example:

I have a type. Under this type, I have assorted methods, some of which have one or more "OnAttribute" attributes attached to them, each defining an event name this method needs to fire on. I'm currently getting a Dictionary of Methods to their relevant lists of event names with this query:

var methods = this.GetType().GetMethods()
    .Where(m => Attribute.IsDefined(m, typeof(OnAttribute)))
    .ToDictionary(m => m,
        m => m.GetCustomAttributes(typeof(OnAttribute), true)
            .Select(a => ((OnAttribute)a).EventName)); //OnAttribute defines an EventName field

The resulting type is IEnumerable<IGrouping<System.Reflection.MethodInfo,IEnumerable<string>>> . However, in order to sign the methods up for their requested events, I need to map each event name to any and all methods asking to execute on them. In other words, I need the previously mentioned type mapped to IEnumerable<IGrouping<string,IEnumerable<System.Reflection.MethodInfo>>> .

I need this:

[On("Event_1")]
[On("Event_2")]
[On("Event_4")]
void Method_1()
{
    ...
}

[On("Event_1")]
void Method_2()
{
     ...
}

[On("Event_2")]
[On("Event_3")]
void Method_3()
{
    ...
}

Which, using my current code, would map to this:

Method_1 => ["Event_1","Event_2","Event_4"]
Method_2 => ["Event_1"]
Method_3 => ["Event_2","Event_3"]

Instead mapped to this:

"Event_1" => [Method_1,Method_2]
"Event_2" => [Method_1,Method_3]
"Event_3" => [Method_2]
"Event_4" => [Method_1]

I'm sure there's some better terminology out there somewhere to describe this; I just don't know what it is.

I attempted using a foreach loop to create a dictionary the ugly way, but as I said, that's really ugly. I also attempted to use SelectMany() with some anonymous types to flatten the Dictionary into a list, and then use GroupBy to re-group that list into my desired dictionary, but that seemed inefficient and messy. I'd like to know the (best) LINQ way of doing this.

I hope that made at least a little bit of sense.

Thanks for any help!

You have to use a combination of SelectMany and GroupBy to get the required result.

Here is a solution that seems to be working:

var methods = this.GetType().GetMethods()
    .Where(m => Attribute.IsDefined(m, typeof(OnAttribute)))
    .SelectMany(m => m.GetCustomAttributes(typeof(OnAttribute), true)
            .Select(a => new { EventName = ((OnAttribute)a).EventName, MethodInfo = m }))
    .GroupBy(g => g.EventName, g => g.MethodInfo);

In this solution, the collection is first flattened to get an intermediate collection of anonymous types containing EventName and the MethodInfo . This intermediate collection of anonymous types if then Grouped by EventName to get groups of Methods by EventName .

To get the reverse mapping from your current dictionary of methods, you can do this:

var reverseMapping = methods
    .SelectMany(kvp => kvp.Value, (kvp, evt) => new { Event = evt, Method = kvp.Key })
    .GroupBy(a => a.Event, a => a.Method);

Then, if you want to dump them out:

foreach (var grouping in reverseMapping.OrderBy(g => g.Key))
{
    Console.WriteLine(string.Format("{0} => {1}", 
        grouping.Key, string.Join(",", grouping.Select(m => m.Name))));
}

Fiddle: https://dotnetfiddle.net/SdE7LD

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