Reputation: 3059
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
{
}
Upvotes: 6
Views: 18144
Reputation: 136
You can declare a Dictionary in the Cache class, use the Animal class type as the key and List<Animal> as the value.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main(string[] args)
{
Cache.AddCacheObj<Dog>(new Dog());
Cache.AddCacheObj<Cat>(new Cat());
var cat = Cache.GetCachedObj<Dog>();
Console.WriteLine("Cat: {0}", cat);
var dog = Cache.GetCachedObj<Cat>();
Console.WriteLine("Dog: {0}", dog);
}
}
public static class Cache
{
static Dictionary<Type, List<Animal>> dict = new Dictionary<Type, List<Animal>>();
public static T GetCachedObj<T>() where T: Animal
{
List<Animal> list;
if (dict.TryGetValue(typeof(T), out list))
{
return list.FirstOrDefault() as T;
}
return null;
}
public static void AddCacheObj<T>(T obj) where T: Animal
{
List<Animal> list;
if (!dict.TryGetValue(typeof(T), out list))
{
list = new List<Animal>();
dict[typeof(T)] = list;
}
list.Add(obj);
}
}
public class Animal
{
public override string ToString()
{
return "This is a " + this.GetType().ToString();
}
}
public class Dog : Animal
{
}
public class Cat : Animal
{
}
The output:
Cat: This is a Dog
Dog: This is a Cat
You can check out the demo code here: https://dotnetfiddle.net/FYkCw7
Upvotes: 0
Reputation: 1265
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.
There is no need to use delegates in this particular problem, because LINQ allows selecting objects by type from IEnumerable
:
public class AnimalCache
private readonly Animals as new HashSet(of Animal)
public function Add(Animal as Animal) as AnimalCache
Animals.Add(Animal)
return me
end function
public function GetAnimals(of AnimalType as Animal) as IEnumerable(of AnimalType)
return Animals.OfType(of AnimalType)
end function
end class
Usage:
dim Cache = new AnimalCache().
Add(new Cat).Add(new Dog).Add(new Cat)
dim CachedCats = Cache.GetAnimals(of Cat)
dim CachedDogs = Cache.GetAnimals(of Dog)
Note that if a Giraffe
class is added into the class hierarchy, AnimalCache
will still work:
public class Giraffe
inherits Animal
end class
dim Cache = new AnimalCache().
Add(new Cat).Add(new Dog).Add(new Giraffe)
dim CachedGiraffes = Cache.GetAnimals(of Giraffe)
Upvotes: 0
Reputation: 1
If You have an instance of the object You want to get default value, You can use this:
public static T GetDefault<T>(T par)
{
return default(T);
}
For example:
System.Drawing.Point p = new Point();
System.Drawing.Point defaultPoint = GetDefault(p);
Upvotes: 0
Reputation: 68660
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>
.
Upvotes: 6
Reputation: 62472
You need to specify the type when declaring method
:
static FirstDelegate<int> method;
Upvotes: 4