简体   繁体   English

通用代表C#

[英]Generic Delegate C#

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 . 但是你的整个设计有一个缺陷:它打破了Liskov替代原则

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. 该LSP指出,如果T (例如, Cat )是的一个亚型Animal ,然后的任何实例Animal可以被替换T没有任何令人惊讶的效果。

Let me ellaborate. 让我说清楚。 Say that you decide to create a new animal, a Giraffe . 假设你决定创造一种新的动物, Giraffe Now, if you call GetCachedObj<Giraffe> , you'll get an exception! 现在,如果你打电话给GetCachedObj<Giraffe> ,你会得到一个例外! The code does not work for any subtype of Animal , the LSP does not hold! 该代码不会任何亚型工作Animal ,该LSP不成立!

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. 注意:我认为Cache不应该是静态的。 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. 相反,您可以使用Singleton模式在应用程序中创建一个缓存实例(每种动物类型),或使用依赖注入(使用Castle Windsor等框架)将缓存注入每个客户端。


old answer 老答案

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. 您可以在声明时将方法的泛型类型参数绑定到特定类型(如提到的@Sean),也可以将封闭类型设置为泛型。

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: 您也可以保留T unbound(不使封闭类型通用),但您必须在方法名称声明T ,如下所示:

public FirstDelegate<T> Method<T>(){...}

Either way, at some point in time, T will be bound to a specific type. 无论哪种方式,在某个时间点, T将绑定到特定类型。 In this case, T will be bound when you create an instance of MyClass (ie, new MyClass<int> ), like you would do with a List<T> . 在这种情况下,当您创建MyClass的实例(即, new MyClass<int> )时,将绑定T ,就像使用List<T>

在声明method时需要指定类型:

static FirstDelegate<int> method;

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

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 : 在这个特定问题中不需要使用委托,因为LINQ允许从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: 请注意,如果将Giraffe类添加到类层次结构中, AnimalCache仍将起作用:

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)

You can declare a Dictionary in the Cache class, use the Animal class type as the key and List<Animal> as the value. 您可以在Cache类中声明Dictionary,使用Animal类类型作为键,使用List <Animal>作为值。

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 您可以在这里查看演示代码: https//dotnetfiddle.net/FYkCw7

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM