简体   繁体   中英

Generics wtih anonymous Delegates expressed a lambdas… Ithink

So I'm trying to do something just slightly beyond the edge of my understanding... in three different ways. Let me explain what I'm trying to do then I'll get to the particulars of my lack of understanding.

I have several Dictionaries, that contain objects I need to generate reports. they are all

ConcurrentDictionary< Int64, List< EarningsReportCV>>

where earnings CV is a custom object that only contains properties(custom View model).

I have three of these dictionaries...and the code to init them is almost identical, they each just contain a different class CV.

Here is one example:

private void BuildDictForAllEarn( List<EarningsReportCV> list, ConcurrentDictionary<Int64, List<EarningsReportCV>> theDict )
{
    foreach ( EarningsReportCV cv in list )
    {
        if ( theDict.ContainsKey( Convert.ToInt64( cv.Ssn ) ) )
        {
            //append in list already in Dict - EWB
            theDict[ Convert.ToInt64( cv.Ssn ) ].Add( cv );
        }
        else
        {
            //insert inital list into the Dict - EWB
            List<EarningsReportCV> cvList = new List<EarningsReportCV>();
            cvList.Add( cv );
            theDict.AddOrUpdate( Convert.ToInt64( cv.Ssn ), cvList, ( foundkey, oldvalue  => cvList );
        }
    }
}

All three Dictionaries are keyed off a string <T>.Ssn

Instead of having copy and paste code with just the types of the CV changing, I want to make a generic method. To do this I need to pass in a anonymous delegate that allows me to generically take the passed in type T and get it's .Ssn property to use as the key.

I googled and thought and read and got this far...

Generic:

 private void BuildDict<T>( List<T> list, ConcurrentDictionary<Int64, List<T>> theDict, Func<T, string> getIndexFunc )

 {
     foreach ( T cv in list )
     {
         if ( theDict.ContainsKey( Convert.ToInt64( getIndexFunc( cv ) ) ) )
         {
             //append in list already in Dict - EWB
             theDict[ Convert.ToInt64( getIndexFunc( cv ) ) ].Add( cv );
         }
         else
         {
             //insert inital list into the Dict - EWB
             List<EarningsReportCV> cvList = new List<EarningsReportCV>();
             cvList.Add( cv );
             theDict.AddOrUpdate( Convert.ToInt64( getIndexFunc( cv ) ), cvList, ( foundkey, oldvalue ) => cvList );
         }
     }
 }

which I call thusly

private void BuildDictForAllEarnLAMBDA( List<EarningsReportCV> list, ConcurrentDictionary<Int64, List<EarningsReportCV>> theDict )
{
    BuildDict<EarningsReportCV>( list, theDict, ( T ) => { return T.Ssn; } );// fix this lambda as paramether stuff...- EWB
}

I think I get everything there except the 3r d parameter, which I want to pass in as a lambda to look up the .Ssn property of the generic type <T> .

When I compile it , I get these errors..

Error 43 Argument 1: cannot convert from 'T' to 'EFRGPayroll3G.CV.EarningsReportCV' C:\\Users\\Brown.Ericw\\Documents\\Visual Studio 2013\\Projects\\WindowsService1\\WindowsService1\\BLL\\RazorReportRenderBLL.cs 406 33 WindowsService1
Error 45 Argument 2: cannot convert from 'System.Collections.Generic.List<EFRGPayroll3G.CV.EarningsReportCV>' to 'System.Func<long,System.Collections.Generic.List<T>>' C:\\Users\\Brown.Ericw\\Documents\\Visual Studio 2013\\Projects\\WindowsService1\\WindowsService1\\BLL\\RazorReportRenderBLL.cs 407 81 WindowsService1
Error 46 Argument 3: cannot convert from 'lambda expression' to 'System.Func<long,System.Collections.Generic.List<T>,System.Collections.Generic.List<T>>' C:\\Users\\Brown.Ericw\\Documents\\Visual Studio 2013\\Projects\\WindowsService1\\WindowsService1\\BLL\\RazorReportRenderBLL.cs 407 89 WindowsService1
Error 44 The best overloaded method match for 'System.Collections.Concurrent.ConcurrentDictionary<long,System.Collections.Generic.List<T>>.AddOrUpdate(long, System.Func<long,System.Collections.Generic.List<T>> System.Func<long,System.Collections.Generic.List<T>, System.Collections.Generic.List<T>>)' has some invalid arguments C:\\Users\\Brown.Ericw\\Documents\\Visual Studio 2013\\Projects\\WindowsService1\\WindowsService1\\BLL\\RazorReportRenderBLL.cs 407 21 WindowsService1
Error 42 The best overloaded method match for 'System.Collections.Generic.List<EFRGPayroll3G.CV.EarningsReportCV>.Add(EFRGPayroll3G.CV.EarningsReportCV)' has some invalid arguments C:\\Users\\Brown.Ericw\\Documents\\Visual Studio 2013\\Projects\\WindowsService1\\WindowsService1\\BLL\\RazorReportRenderBLL.cs 406 21 WindowsService1

and at that point I no longer understand what's going on...my brain is just full... what do I do to take this to a functional piece of code. I'm looking for both what to do, and what it is I need to grok to wrap my head around this and best of all good articles to explain it to me... Any even small glimmerings of understanding are greatly appreciated.

The line List<EarningsReportCV> cvList = new List<EarningsReportCV>(); Should be using T , not EarningsReportCV .

You made your function generic but simply forgot to change those two instances of the old concrete class to the generic type. The method compiles after that change.

Having said that, there are several issues that you should probably change in your function.

First and foremost, it appears to attempt to be safe to be called from multiple threads, but it's not. A key could be added or removed in another thread after you check to see if it exists, resulting in items being dropped on the floor.

The premise of your program is to add an item if it's not there, and update it if it is. That exactly what AddOrUpdate is designed to do, atomically. You should simply be calling that once, rather than what you're doing. It even makes the code simpler.

private void BuildDict<T>(List<T> list,
    ConcurrentDictionary<long, List<T>> theDict,
    Func<T, string> getIndexFunc)
{
    foreach (T cv in list)
    {
        theDict.AddOrUpdate(Convert.ToInt64(getIndexFunc(cv)),
            key => new List<T>() { cv },
            (foundkey, oldvalue) =>
            {
                oldvalue.Add(cv);
                return oldvalue;
            });
    }
}

There are some other changes that you can make to improve the code as well. Since you only ever iterate list and never do anything else, you can make that parameter an IEnumerable , allowing it to be any type of sequence beyond just lists.

If you're program is designed to be accessing and manipulating theDict from multiple threads, it's very likely that the inner lists shouldn't be lists, but should be a collection designed to be accessed from multiple threads, such as a ConcurrentBag .

Since the delegate you're accepting really wants a long , not a string , that's really what it should accept, rather than accepting a string and trying to convert it.

This gives us:

private void BuildDict<T>(IEnumerable<T> sequence,
    ConcurrentDictionary<long, ConcurrentBag<T>> theDict,
    Func<T, long> keySelector)
{
    foreach (T cv in sequence)
    {
        theDict.AddOrUpdate(keySelector(cv),
            key => new ConcurrentBag<T>() { cv },
            (foundkey, oldvalue) =>
            {
                oldvalue.Add(cv);
                return oldvalue;
            });
    }
}

Since all three use .Ssn you don't need a access Func<> . You need to tell the method they all have an Ssn:

  interface IHasSsn
  {
       string Ssn;
  }

private void BuildDict<T>( List<T> list, 
                           ConcurrentDictionary<Int64, List<T>> theDict)
   where T : IHasSsn
{
    foreach ( T cv in list )
    {
        long ssn = Convert.ToInt64( cv.Ssn );
        if ( theDict.ContainsKey(ssn) )
        {
            theDict[ssn].Add( cv );
        }
        else
        {
            var cvList = new List<T>();
            cvList.Add( cv );
            theDict.AddOrUpdate(ssn, cvList, ( foundkey, oldvalue  => cvList );
        }
    }
}

And make sure each ReportCV implements IHasSsn

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