C#泛型:通配符
我是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); } 现在我想要一本包含这些动物美容师
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
相反,那么您可以将nIAnimalGroomer
转换为IList
举一个更现实的例子,想想IEnumerable
vsIComparer
。一个狗序列可以用作一个动物序列;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 = ...;