委托可以将泛型作为参数

时间:2014-05-15 13:37:06

标签: c# generics

我创建了一个与我的缓存一起使用以获取缓存项的类。如果项目未缓存,则调用函数以获取实际值。

这个类有八个方法,除了它们调用的函数外,它们的代码几乎相同。我创建了一个名为GetObject的函数,如果它无法在类中找到项目,则会调用委托。

由于以下错误,我无法编译代码:

  

参数2:无法转换   'System.Collections.Generic.List<string>'到   'MyFunction<System.Collections.Generic.List<string>>'。

我做错了什么,或者我正在尝试做一些无法做到的事情?

这是我正在尝试的代码。

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;
 }

解决方案

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));
}

3 个答案:

答案 0 :(得分:3)

您传递的是函数的结果而不是函数本身。您可以像这样使用lambda:

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

你也有这一行:

foundItems = f as T;

这里你试图将函数本身转换为它的返回类型,这将不起作用。相反,你可以这样做:

foundItems = f(name);

但现在您的问题是您必须将名称传递给GetObject,否则无法在需要的地方访问该名称。这样做的原因是MyFunctionstring之间存在不匹配,以及您真正想要的是什么,这是一个可以在GetObject内进行评估而不需要name参数的函数被传入。

所以你应该做的是将你的代表改为:

public delegate T MyFunction<T>(); 

或者完全取消委托,并将f参数设为Func<T>

使用这些选项之一,您可以传入lamba而不需要参数:

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

并评估它:

foundItems = f();

请注意,创建一个lambda将其传递给另一个方法只是为了评估它而不是直接传递结果,这有点迂回。因此,除非在某些情况下您需要执行此操作,否则您可能希望更改f参数以取代T类型。在这种情况下,我怀疑你这样做是为了懒惰地评估函数,这样你就不必评估结果是否已经缓存了。这可能是一个正当的理由,假设您没有过早优化性能。

答案 1 :(得分:2)

您没有创建委托。实际上,在调用GetObject之前,您正在评估该方法。轻松修复:

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

另请注意,在此方案中,您对sitename要做的事情并不明显;你可能代之以:

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

答案 2 :(得分:0)

这是一个完整的例子

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));
    }
}

如果你看一下方法Test()和GetObject(),你可以注意到3个有趣的事情:

  1. 您不必在GetObject()上指定泛型类型,因为编译器会从GetCulture()
  2. 中推断它
  3. x参数用作委托函数的输入,就像方法一样 GetObject可以使用&#34;键&#34;并将其传递给委托函数。
  4. 我用&#34; Func&#34;替换你的委托功能带有字符串输入和列表输出。