32

I want to create a generic to which I can pass a function as a parameter, however this function may include parameters itself so...

int foo = GetCachedValue("LastFoo", methodToGetFoo)

Such that:

protected int methodToGetFoo(DateTime today)
{ return 2; // example only }

Essentially I want to have a method that will check the cache for a value, otherwise will generate the value based on the passed in method.

Thoughts?

klkitchens
  • 1,202
  • 2
  • 16
  • 39
  • I don't know how to do this, but as a note, this question doesn't seem to have anything to do with generics as far as I can tell. – recursive Mar 02 '09 at 21:25
  • It kind of does, a little bit, maybe. Probably "delegates" should replace "generics." – mqp Mar 02 '09 at 21:29
  • Sorry, I was creating a generic method to handle this, so I sorta assumed it was related. But the passing is truly delegate related, so I will update. Thanks – klkitchens Mar 02 '09 at 21:51

5 Answers5

52

It sounds like you want a Func<T>:

T GetCachedValue<T>(string key, Func<T> method) {
     T value;
     if(!cache.TryGetValue(key, out value)) {
         value = method();
         cache[key] = value;
     }
     return value;
}

The caller can then wrap this in many ways; for simple functions:

int i = GetCachedValue("Foo", GetNextValue);
...
int GetNextValue() {...}

or where arguments are involved, a closure:

var bar = ...
int i = GetCachedValue("Foo", () => GetNextValue(bar));
jgillich
  • 71,459
  • 6
  • 57
  • 85
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Thanks I was very close, but could not figure how to get the parms right in the generic. The last option, with the closure was the key! Works like a charm. – klkitchens Mar 02 '09 at 21:49
  • 4
    Really old one here but the closure part was just what I was looking for – Fredrik Stolpe Aug 20 '15 at 11:38
16

Use System.Action and a lambda expression (anonymous method). For example:

public void myMethod(int integer) {     
    // Do something
}

public void passFunction(System.Action methodWithParameters) {
    // Invoke
    methodWithParameters();
}

// ...

// Pass anonymous method using lambda expression
passFunction(() => myMethod(1234));
CarenRose
  • 1,266
  • 1
  • 12
  • 24
  • Do I understand it right, () => myMethod(1234) is wrapping the parametrized method in a non-parametrized method as a return value? – aDoubleSo May 24 '19 at 05:05
5

You can create your own delegate, but in C# 3.0 you may find it more convenient to use the built-in Func<T> delegate family to solve this problem. Example:

public int GetCachedValue(string p1, int p2,
                          Func<DateTime, int> getCachedValue)
{
    // do some stuff in here
    // you can call getCachedValue like any normal function from within here
}

This method will take three arguments: a string, an int, and a function that takes a DateTime and returns an int. For example:

int foo = GetCachedValue("blah", 5, methodToGetFoo);   // using your method
int bar = GetCachedValue("fuzz", 1, d => d.TotalDays); // using a lambda

Different Func<T, U, V...> etc. types exist in the framework to accommodate methods with different amounts of arguments.

mqp
  • 70,359
  • 14
  • 95
  • 123
3

Create a delegate for the method methodToGetFoo

public delegate object GenerateValue(params p);
public event GenerateValue OnGenerateValue;

Define GetCachedValue to use the delegate

int GetCachedValue(string key, GenerateValue functionToCall);

Then in the implementation of OnGenerateValue you can check the param's.

Dead account
  • 19,587
  • 13
  • 52
  • 82
2

Here is something simple I started that can be taken a bit further (as I did for a commercial project).

In my case this was to cache web service calls, and was used something like:

WebService ws = new WebService();
var result = ws.Call( x => x.Foo("bar", 1));  // x is the ws instance
leppie
  • 115,091
  • 17
  • 196
  • 297