C#泛型:通配符

C#泛型:通配符,c#,generics,C#,Generics,我是c#世界的新手,我正试着用泛型来概括我的想法。这是我目前的问题: public Interface IAnimal{ string getType(); } public Interface IAnimalGroomer<T> where T:IAnimal{ void groom(T); } 公共接口IAnimal{ 字符串getType(); } 公共接口iAnimalRoomer,其中T:IAnimal{ 无效新郎(T); } 现在我想要一本包含这些动物美容师

我是c#世界的新手,我正试着用泛型来概括我的想法。这是我目前的问题:

public Interface IAnimal{
  string getType();
}

public Interface IAnimalGroomer<T> where T:IAnimal{
  void groom(T);
}
公共接口IAnimal{
字符串getType();
}
公共接口iAnimalRoomer,其中T:IAnimal{
无效新郎(T);
}
现在我想要一本包含这些动物美容师的字典。我该怎么做?在java中,我可以这样做:

HashMap<String,IAnimalGroomer<?>> groomers = new HashMap<>();

HashMap根据我的理解,在这种情况下,不能将类型约束放在参数中。这意味着您可能需要进行装箱和拆箱。您可能需要使用普通接口

public interface IAnimal{
  string GetType();
}

public interface IAnimalGroomer{
  void Groom(IAnimal dog);
}

public class Dog : IAnimal
{
    public string GetType()
    {
        return "DOG";
    }

    public void ClipNails()
    {

    }
}

public class DogGroomer : IAnimalGroomer
{
    public void Groom(IAnimal dog)
    {
        if (dog is Dog)
        {
            (dog as Dog).ClipNails();
        }
        else {
             // something you want handle.
        }
    }
}




public class Program
{
    private List<IAnimalGroomer> groomers = new List<IAnimalGroomer>();

    public void doSomething()
    {
        groomers.Add(new DogGroomer());
    }
}
公共接口IAnimal{
字符串GetType();
}
公共接口IAnimalGroomer{
无效新郎(动物狗);
}
公家犬:IAnimal
{
公共字符串GetType()
{
返回“狗”;
}
公共作废剪贴簿()
{
}
}
公共类狗美容师:IAnimalGroomer
{
公共空间新郎(动物犬)
{
如果(狗就是狗)
{
(像狗一样的狗)。ClipNails();
}
否则{
//你想要处理的事情。
}
}
}
公共课程
{
私有列表梳理器=新列表();
公共无效剂量测定法()
{
添加(新的狗修饰器());
}
}

或者您需要另一种技术设计来解决您的问题

以下是一些有效的代码。我添加了一些类,并将AnimalGroomer切换为抽象类而不是接口:

class Program
{
    static void Main(string[] args)
    {
        var dict = new Dictionary<string, IGroomer>();
        dict.Add("Dog", new DogGroomer());

        // use it 
        IAnimal fido = new Dog();
        IGroomer sample = dict["Dog"];
        sample.Groom(fido);


        Console.WriteLine("Done");
        Console.ReadLine();
    }
}

// actual implementation
public class Dog : IAnimal { }

public class DogGroomer : AnimalGroomer<Dog>
{
    public override void Groom(Dog beast)
    {
        Console.WriteLine("Shave the beast");
    }
}

public interface IAnimal {

}

public interface IGroomer
{
    void Groom(object it);
}

public abstract class AnimalGroomer<T> : IGroomer where T : class, IAnimal
{
  public abstract void Groom(T beast);

  public void Groom(object it)
  {
      if (it is T)
      {
          this.Groom(it as T);
          return;
      }
      throw new ArgumentException("The argument is not a " + typeof(T).GetType().Name);
  }
}
类程序
{
静态void Main(字符串[]参数)
{
var dict=新字典();
dict.Add(“狗”,新的狗美容师());
//使用它
IAnimal fido=新狗();
IGroomer样本=dict[“狗”];
样本:新郎(fido);
控制台。写入线(“完成”);
Console.ReadLine();
}
}
//实际执行
公家犬:IAnimal{}
公共类狗美容师:动物美容师
{
公马(狗兽)
{
Console.WriteLine(“剃掉野兽”);
}
}
公共接口IAnimal{
}
公共接口室内机
{
无效新郎(反对它);
}
公共抽象类AnimalGroomer:IGroomer其中T:class,IAnimal
{
公共抽象空新郎(T beast);
公共无效新郎(反对它)
{
如果(它是T)
{
这个。新郎(它是T);
返回;
}
抛出新ArgumentException(“参数不是“+typeof(T).GetType().Name”);
}
}

如果有任何问题,请告诉我。

有两个接口,
IEnumerable
IEnumerable
,这两个接口与您试图实现的目标非常接近。因此,您可以使用一个类似于
dictionary
的字典,它可以包含as值
IEnumerable
IEnumerable
,等等。这里的技巧是从
IAnimalGroomer
派生
IAnimalGroomer
,这是一个非通用接口

public interface IAnimal{
  string GetType();
}

public interface IAnimalGroomer{
  void Groom(IAnimal dog);
}

public class Dog : IAnimal
{
    public string GetType()
    {
        return "DOG";
    }

    public void ClipNails()
    {

    }
}

public class DogGroomer : IAnimalGroomer
{
    public void Groom(IAnimal dog)
    {
        if (dog is Dog)
        {
            (dog as Dog).ClipNails();
        }
        else {
             // something you want handle.
        }
    }
}




public class Program
{
    private List<IAnimalGroomer> groomers = new List<IAnimalGroomer>();

    public void doSomething()
    {
        groomers.Add(new DogGroomer());
    }
}
编辑:

例如,根据您的请求,在创建名为
IAnimalGroomer
的接口后,使用以下命令:

public interface IAnimalGroomer{
}
,如果更改以下行:

public interface IAnimalGroomer<T> where T:IAnimal{
private List<IAnimalGroomer<IAnimal>> groomers = new List<IAnimalGroomer<IAnimal>>();
公共接口iAnimalRoomer,其中T:IAnimal{

公共接口IAnimalGroomer:IAnimalGroomer其中T:IAnimal{
这行字是:

public interface IAnimalGroomer<T> where T:IAnimal{
private List<IAnimalGroomer<IAnimal>> groomers = new List<IAnimalGroomer<IAnimal>>();
private-List-gloomers=new-List();

private-List-gloomers=new-List();
您的代码应该编译并运行。

您需要的是调用站点协方差,这不是C#支持的特性。C#4及以上版本支持通用方差,但不支持调用站点方差

但是,这对您没有帮助。您希望将狗美容师放在动物美容师列表中,但这在C#中不起作用。狗美容师不能用于任何需要动物美容师的环境中,因为狗美容师只能给狗美容,但动物美容师也可以给猫美容。也就是说,您希望界面当不能以协变方式安全使用时,应为协变

然而,您的
IAnimalGroomer
界面可以是相反的:动物美容师可以在需要狗美容师的环境中使用,因为动物美容师可以为狗美容师。如果您通过在
T
的声明中添加
in
使
IAnimalGroomer
相反,那么您可以将n
IAnimalGroomer
转换为
IList

举一个更现实的例子,想想
IEnumerable
vs
IComparer
。一个狗序列可以用作一个动物序列;
IEnumerable
是协变的。但是一个动物序列不能用作一个狗序列;其中可能有一只老虎

相比之下,比较动物的比较器可以用作狗的比较器;
IComparer
是相反的。但狗的比较器不能用于比较动物;有人可以尝试比较两只猫

如果仍不清楚,请阅读常见问题解答:


然后回来问更多的问题,如果你有问题的话。

我知道这个问题已经被讨论过了,但我仍然想回答。这个列表在这里是一条红鲱鱼,你使用它并不重要

这不起作用的原因是,因为
IAnimalGroomer
本身不是协变的,并且它不能显式地变为协变的,因为
groom(t)
method
。在一般情况下,将
IA
强制转换为
IA
是非法的,或者换言之,默认情况下,通用接口是不协变的。
列表。Add
方法是触发从
DogGroomer
强制转换的方法(即
IAnimalGroomer
)例如,这仍然不起作用:

IAnimalGroomer<Dog> doggroomer = new DogGroomer(); // fine
IAnimalGroomer<IAnimal> animalgroomer = doggroomer; // invalid cast, you can explicitly cast it
                                      // in which case it fails at run time
没有使用序列,但是如果它是合法的,代码仍然会破坏类型安全性

这种类型的强制转换是允许的,但是由它引起的错误会发生在运行时,我认为这是不可取的

如果将类型参数T标记为“
IAnimalGroomer<Dog> doggroomer = new DogGroomer(); // fine
IAnimalGroomer<IAnimal> animalgroomer = doggroomer; // invalid cast, you can explicitly cast it
                                      // in which case it fails at run time
public interface IAnimal
{
    string getType();
}

public interface IAnimalGroomer<T> where T:IAnimal
{
    void groom(T t);
}

public class  Dog : IAnimal
{
    public string getType() { return "DOG"; }

    public void clipNails() { }
}

public class DogGroomer : IAnimalGroomer<Dog>
{
    public void groom(Dog dog)
    {
        dog.clipNails();
    }
}

public class Cat : IAnimal
{
    public string getType() { return "CAT"; }

    public void clipNails() { }
}

public class CatGroomer : IAnimalGroomer<Cat>
{
    public void groom(Cat cat)
    {
        cat.clipNails();
    }
}

public class Program
{
    static void Main(string[] args)
    {
        // this is fine.
        IAnimalGroomer<Dog> doggroomer = new DogGroomer();
        // this is an invalid cast, but let's imagine we allow it! 
        IAnimalGroomer<IAnimal> animalgroomer = doggroomer;
        // compile time, groom parameter must be IAnimal, so the following is legal, as Cat is IAnimal
        // but at run time, the groom method the object has is groom(Dog dog) and we're passing a cat! we lost compile-time type-safety.
        animalgroomer.groom(new Cat());                                  
    }
}
public interface IAnimal
{
}

public class Dog : IAnimal
{
}

public class Cat : IAnimal
{
}

public class BigBadWolf : IAnimal
{
}

//I changed `IAnimalGroomer` to an abstract class so you don't have to implement the `AnimalType` property all the time.
public abstract class AnimalGroomer<T> where T:IAnimal
{
    public Type AnimalType { get { return typeof(T); } }
    public abstract void Groom(T animal);
}

public class CatGroomer : AnimalGroomer<Cat>
{
    public override void Groom(Cat animal)
    {
        Console.WriteLine("{0} groomed by {1}", animal.GetType(), this.GetType());
    }
}

public class DogGroomer : AnimalGroomer<Dog>
{
    public override void Groom(Dog animal)
    {
        Console.WriteLine("{0} groomed by {1}", animal.GetType(), this.GetType());
    }
}

public class AnimalClinic
{
    private Dictionary<Type, dynamic> groomers = new Dictionary<Type, dynamic>();

    public void EmployGroomer<T>(AnimalGroomer<T> groomer) where T:IAnimal
    {
        groomers.Add(groomer.AnimalType, groomer);
    }

    public void Groom(IAnimal animal)
    {       
        dynamic groomer;
        groomers.TryGetValue(animal.GetType(), out groomer);

        if (groomer != null)
            groomer.Groom((dynamic)animal);
        else
            Console.WriteLine("Sorry, no groomer available for your {0}", animal.GetType());
    }
}
var animalClinic = new AnimalClinic();
animalClinic.EmployGroomer(new DogGroomer());
animalClinic.EmployGroomer(new CatGroomer());
animalClinic.Groom(new Dog());
animalClinic.Groom(new Cat());
animalClinic.Groom(new BigBadWolf());
void Main()
{
    var clinic = new AnimalClinic();

    clinic.Add(new CatGroomer());
    clinic.Add(new DogGroomer());
    clinic.Add(new MeanDogGroomer());    

    clinic.Groom(new Cat()); //Purr
    clinic.Groom(new Dog()); //Woof , Grrr!
}

public interface IAnimal {}
public interface IGroomer {}

public class Dog : IAnimal
{    
    public string Woof => "Woof";
    public string Growl => "Grrr!";        
}

public class Cat : IAnimal
{
    public string Purr => "Purr";        
}

public interface IGroomer<T> : IGroomer where T : IAnimal
{
    void Groom(T animal);
}

public class DogGroomer : IGroomer<Dog>    
{
    public void Groom(Dog dog) => Console.WriteLine(dog.Woof);      
}

public class MeanDogGroomer : IGroomer<Dog>    
{
    public void Groom(Dog dog) => Console.WriteLine(dog.Growl);     
}

public class CatGroomer : IGroomer<Cat>
{     
    public void Groom(Cat cat) => Console.WriteLine(cat.Purr);
}

public class AnimalClinic
{
    private TypedLookup<IGroomer> _groomers = new TypedLookup<IGroomer>();

    public void Add<T>(IGroomer<T> groomer) where T : IAnimal 
      => _groomers.Add<T>(groomer);

    public void Groom<T>(T animal) where T : IAnimal 
      => _groomers.OfType<T, IGroomer<T>>().ToList().ForEach(g => g.Groom(animal));    
}

public class TypedLookup<T> : Dictionary<Type, IList<T>>
{
    public void Add<TType>(T item) 
    {   
        IList<T> list;
        if(TryGetValue(typeof(TType), out list))
            list.Add(item); 
        else
            this[typeof(TType)] = new List<T>{item};
    }

    public IEnumerable<TRet> OfType<TType, TRet>() => this[typeof(TType)].Cast<TRet>();
    public TRet First<TType, TRet>() => this[typeof(TType)].Cast<TRet>().First();
}
public class AnimalGroomerClinic {
    public Dictionary<string, object> animalGroomers = new Dictionary<string, object>();

    public void employGroomer<T>(IAnimalGroomer<T> groomer) where T : IAnimal {
        animalGroomers.Add(groomer.getAnimalType(), groomer);
    }
    public void Groom<T>(T animal) where T : IAnimal {
        // Could also check here if the 'as' operator returned null,
        // which might happen if you don't have the specific groomer
        (animalGroomers[animal.getAnimalType()] as IAnimalGroomer<T>).groom(animal);
    }
}
class AnimalGroomerClinic {
    public Map<String, Object> animalGroomers = new HashMap<>();

    public <T extends IAnimal> void employGroomer(IAnimalGroomer<T> groomer) {
        animalGroomers.put(groomer.getAnimalType(), groomer);
    }

    @SuppressWarnings("unchecked")
    public <T extends IAnimal> void Groom(T animal) {
        ((IAnimalGroomer<T>) animalGroomers.get(animal.getAnimalType())).groom(animal);
    }
}
public interface IAnimalGroomerSuper { 
    // A stub interface
}

public interface IAnimalGroomer<T> : IAnimalGroomerSuper where T : IAnimal {...}
public Dictionary<string, IAnimalGroomerSuper> animalGroomers = ...;