I've created a class that works with my cache to get cached items. If the items are not cached then it calls a function to get the actual value.

This class has eight methods, all with almost identical code except for the function they call. I've created a function called GetObject which takes a delegate to call if it can't find an item in the class.

I can't get my code to compile because of the following error:

Argument 2: cannot convert from 'System.Collections.Generic.List<string>' to 'MyFunction<System.Collections.Generic.List<string>>'.

Am I doing something wrong or am I'm trying to do something that can't be done?

Here's the code I'm trying.

public delegate T MyFunction<T>(string s); 

public T GetCultures<T>(string s) where T : class {
    return NewListOfStrings(s) as T;
}


public List<string> NewListOfStrings(string s) {
    return new List<string> { s };
}


public List<string> GetListOfStrings(string sitename) {
    string key = cachingService.CreateValidKey("stringvalue");

     //This is the line that fails to compile
     var foundItems = GetObject<List<string>>(key, 
                                      GetCultures<List<string>>(sitename));

     return foundItems;
}



public T GetObject<T>(string key, MyFunction<T> f) where T : class {

    T foundItems = (T)cachingService.GetCachedItem(key);

    if (foundItems == null) {
        lock (key) {
            foundItems = (T)cachingService.GetCachedItem(key);

            if (foundItems == null) {
                foundItems = f as T;

                if (foundItems != null) {
                     cachingService.SetCachedItem(key, foundItems, 5, 
                                                        Constants.MINUTES);
                }
            }
         }
     }

     return foundItems;
 }

Solution

public T GetObject<T>(string key, Func<T> getFromRepository) where T : class {

    T foundItems = (T)cachingService.GetCachedItem(key);

    if (foundItems == null) {
        lock (key) {
           foundItems = (T)cachingService.GetCachedItem(key);

           if (foundItems == null) {
               foundItems = getFromRepository() as T;

               if (foundItems != null) {
                   cachingService.SetCachedItem(key, foundItems, 5, 
                                                      Constants.MINUTES);
               }
            }
        }
    }

    return foundItems;
}


public AreaModels.Site GetSiteByName(string sitename) {
   string key = cachingService.CreateValidKey(
                                 string.Format("Site_{0}", sitename));

   return GetObject<AreaModels.Site>(key, 
                                 () => efRepository.GetSiteByName(sitename));
}


public List<AreaModels.Culture> GetCulturesForSite(string sitename) {
   string key = cachingService.CreateValidKey(
                                  string.Format("Cultures_{0}", sitename));

   return GetObject<List<AreaModels.Culture>>(key, 
                       () => efRepository.GetCulturesForSite(sitename));
}


public List<AreaModels.Resource> Resources(string sitename, int appId) {
   string key = cachingService.CreateValidKey(
                                 string.Format("ResourcesFor{0}", sitename));

   return GetObject<List<AreaModels.Resource>>(key, 
               () => efRepository.GetResourcesBySiteAndAppId(sitename, appId));
}
有帮助吗?

解决方案

You're passing the result of the function rather than the function itself. You can use a lambda like so:

var foundItems = GetObject<List<string>>(key, 
                                  name => GetCultures<List<string>>(sitename));

You also have this line:

foundItems = f as T;

Here you're trying to cast the function itself to its return type, which won't work. Instead you could do:

foundItems = f(name);

But now your problem is that you'd have to pass the name into GetObject, because otherwise it won't be accessible where it's needed. The reason for this is there's a mismatch between MyFunction, which takes a string, and what you actually want, which is a function that can be evaluated within GetObject without needing the name parameter to be passed in.

So what you should really do is change your delegate to:

public delegate T MyFunction<T>(); 

Or alternatively get rid of the delegate altogether and have the f parameter be a Func<T>.

With either of these options, you can pass in the lamba with no parameter required:

var foundItems = GetObject<List<string>>(key, 
                                  () => GetCultures<List<string>>(sitename));

And evaluate it like:

foundItems = f();

Note that it's a bit roundabout to create a lambda to pass it into another method just to then evaluate it, rather than just passing the result in directly. So unless there's some reason that you need to do this in some cases, you might instead want to change the f parameter to take a type T instead. In this case I suspect you're doing it to lazily evaluate the function so that you don't have to evaluate if the result is already cached. That would probably be a valid reason, assuming you're not optimizing for performance prematurely.

其他提示

You aren't creating a delegate. You are actually evaluating the method before calling GetObject. Easily fixed:

    var foundItems = GetObject<List<string>>(key,
        name => GetCultures<List<string>>(name));

Note also that it isn't obvious what you want to do with sitename in this scenario; you might instead mean this:

        name => GetCultures<List<string>>(sitename));

Here's a complete example

public class TestDelegate
{
    //You don't need generic here if you always return a list of string
    public List<string> GetCulture(string s) 
    {
        return new List<string> { s };
    }

    public T GetObject<T>(string key, Func<string, T> fn)
    {
        T foundItems = fn(key);
        return foundItems;
    }

    public void Test()
    {
        List<string> test = GetObject("abc", x => GetCulture(x));
    }
}

If you look at the method Test() and GetObject(), you can note 3 interesting things :

  1. You don't have to specify the generic type on GetObject() because the compiler infer it from GetCulture()
  2. The x parameter serves as an input to your delegate function, that way the method GetObject can use the "key" and pass it to the delegate function.
  3. I replace your delegate function by "Func" with a string input and a List output.
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top