Im trying to figure out how to make a generic delegate returning a generic value. My first non-generic scenario looks like this.
delegate int FirstDelegate(int i);
static FirstDelegate method;
static void Main(string[] args)
{
method = ReturnInt;
int i = method(3);
}
static int ReturnInt(int i)
{
return i;
}
No problems here. Everything works fine. However when I make it generic things spin out of control.
delegate T FirstDelegate<T>(T i);
static FirstDelegate<T> method; <--
Already here he starts complaining about type or namespace etc etc not found. Anyone have any ideas of how to get this working?
Edit: My real goal is that I have a cache that can contain many different cache objects. And now I want a single method that is generic so I can get all objects through this one. I could make it return object or a baseclass, but then I still would have to cast each object everywhere its used.
Dog / cat example The non-generic parts are working.. the generic.. not so much
class Program
{
static void Main(string[] args)
{
//Clientside
Cache.method = GetAnimalOnClient;
//not working
Cache.methodGeneric = GetAnimalOnClientGeneric;
var cat = Cache.GetCachedObj(AnimalType.Cat);
var dog = Cache.GetCachedObj(AnimalType.Dog);
//Want do
vad dog = Cache.GetCachedObj<Dog>();
}
private static Animal GetAnimalOnClient(AnimalType type)
{
if (type == AnimalType.Dog)
return Cache._Dogs.First();
else
return Cache._Cats.First();
}
/// <summary>
/// This is the one I want to use
/// </summary>
private static T GetAnimalOnClientGeneric<T>() where T: Animal
{
if (typeof(T) == typeof(Cat))
return Cache._Cats.First() as T;
return Cache._Dogs.First() as T;
}
}
public enum AnimalType
{
Dog,
Cat
}
public static class Cache
{
delegate Animal GetCacheObjectDelegate(AnimalType type);
public static GetCacheObjectDelegate method;
delegate Animal GetCacheObjectDelegate<T>() where T : Animal;
public static GetCacheObjectDelegate<T> methodGeneric; //<--Complains here
public static List<Dog> _Dogs = new List<Dog>();
public static List<Cat> _Cats = new List<Cat>();
public static Animal GetCachedObj(AnimalType type)
{
return method(type);
}
public static T GetCachedObj<T>() where T: Animal
{
return methodGeneric<T>(); //NOPE
}
}
public class Animal
{
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
You're overcomplicating things.
public static class Cache
{
private static List<Dog> _dogs = new List<Dog>();
private static List<Cat> _cats = new List<Cat>();
public static TAnimal GetCachedObj<TAnimal>() where T: Animal
{
if(TAnimal == typeof(Dog))
return (TAnimal) _dogs.First();
else if (TAnimal == typeof(Cat))
return (TAnimal) _cats.First();
else throw new InvalidOperationException("Invalid generic type argument");
}
}
But your whole design has a flaw: it breaks the Liskov Substitution Principle.
The LSP states that if T
(for example, Cat
) is a subtype of Animal
, then any instance of Animal
can be replaced with T
without any surprising effects.
Let me ellaborate. Say that you decide to create a new animal, a Giraffe
. Now, if you call GetCachedObj<Giraffe>
, you'll get an exception! The code does not work for any subtype of Animal
, the LSP does not hold!
Instead you should make the cache class generic, and use a cache instance for every kind of animal
public class Cache<T> where T: Animal
{
private static List<T> _animals = new List<T>();
public T GetCachedObj()
{
return _animals.First();
}
}
var dogsCache = new Cache<Dog>();
Dog dog = dogsCache.GetCachedObj();
var catsCache = new Cache<Cat>();
Cat cat = catsCache.GetCachedObj();
This will always work for any kind of animal.
Note: I believe Cache
shouldn't be static. Instead, you can use the Singleton pattern to have one single cache instance across the application (per animal type), or use Dependency Injection (with a framework such as Castle Windsor) to inject a cache into every client.
You either bind the method's generic type argument to a specific type at declaration-time (as @Sean mentioned), or you make the enclosing type generic as well.
public class MyClass<T>
{
public FirstDelegate<T> Method(){...}
}
You can also leave T
unbound (without making the enclosing type generic), but you'll have to declare T
after the method name, like so:
public FirstDelegate<T> Method<T>(){...}
Either way, at some point in time, T
will be bound to a specific type. In this case, T
will be bound when you create an instance of MyClass
(i.e., new MyClass<int>
), like you would do with a List<T>
.
You need to specify the type when declaring method
:
static FirstDelegate<int> method;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With