РЕДАКТИРОВАТЬ: переписать ответ после различных изменений в вопросе
Как уже было сказано (см. Джастин), вы не можете просто использовать дженерики, потому что компилятору необходимо знать во время компиляции, какие типы объектов поддерживаются. Также до сих пор не ясно, чего вы пытаетесь достичь с помощью генериков. Если это сохранить набор текста, то это, вероятно, не решение вашей проблемы ...
Тем не менее, я думаю, что ваша проблема в том, что у вас есть два спрайта неизвестного типа (возможно, из коллекции или чего-то еще):
Sprite a; // may be SpriteA, may be SpriteB
Sprite b; // may be SpriteA, may be SpriteB
Вы хотите организовать столкновение между двумя спрайтами, но для этого вам нужно знать типы обоих спрайтов, чтобы вы могли вызывать соответствующую функцию на вашем HitHelper:
public static void Hit(SpriteA a, SpriteB b)
Чтобы это работало, вам нужно определить типы sprite1 и sprite2, которые вы пытались сделать, используя шаблон посетителя, и он не работал. Это связано с тем, что вы можете разрешить только один параметр за раз (вы можете определить a или b, но не оба сразу). Следовательно, вам необходимо выполнить двойную диспетчеризацию дважды (один раз для каждой переменной), чтобы можно было разрешить оба параметра.
Пример того, как это сделать, показан ниже (вполне может быть лучший подход, предложения приветствуются).
// Define some SpriteTypes, that support an Invoke/visit method
abstract class Sprite {
public abstract TResult Invoke<TResult>(ISpriteInvoker<TResult> invoker);
}
class SpriteA : Sprite {
public override TResult Invoke<TResult> (ISpriteInvoker<TResult> invoker){
return invoker.Invoke(this);
}
}
class SpriteB : Sprite {
public override TResult Invoke<TResult> (ISpriteInvoker<TResult> invoker){
return invoker.Invoke(this);
}
}
// Define Invoker / visit interface
// Has to return a result to support the way it's used later (far from ideal
// it would be nice if there was a way to pass 'void' as the result)
interface ISpriteInvoker<TResult>{
// note, one invoke overload for each type of supported sprite
TResult Invoke(SpriteA sprite);
TResult Invoke(SpriteB sprite);
}
// Define interface for hitter
interface IHitter {
// Note, one overload for each type of sprite that can be hit
int Hit(SpriteA sprite);
int Hit(SpriteB sprite);
}
// Define some Hitter classes (one for each type of sprite that
// can do the hitting). It would be nice if this could be
// Hitter<TSprite>, however as previously stated, this won't work
// because the compiler doesn't support where TSprite : (SpriteA or SpriteB)
// at least not that I can find..
class SpriteAHitter : IHitter {
SpriteA _sprite;
public SpriteAHitter(SpriteA sprite) {
_sprite = sprite;
}
public int Hit(SpriteA sprite)
{
HitHelper.Hit(_sprite, sprite);
return 0;
}
public int Hit(SpriteB sprite)
{
HitHelper.Hit(_sprite, sprite);
return 0;
}
}
class SpriteBHitter : IHitter {
SpriteB _sprite;
public SpriteBHitter(SpriteB sprite) {
_sprite = sprite;
}
public int Hit(SpriteA sprite)
{
HitHelper.Hit(_sprite, sprite);
return 0;
}
public int Hit(SpriteB sprite)
{
HitHelper.Hit(_sprite, sprite);
return 0;
}
}
// Invoker that takes in a sprite and creates
// the appropriate Hitter wrapper.
class HitterCreator : ISpriteInvoker<IHitter> {
public IHitter Invoke(SpriteA sprite) {
return new SpriteAHitter(sprite);
}
public IHitter Invoke(SpriteB sprite) {
return new SpriteBHitter(sprite);
}
}
// Invoker that is constructed with a hitter
// and uses it to kick off the appropriate collison
class HitActioner : ISpriteInvoker<int> {
IHitter _hitter;
public HitActioner(IHitter hitter) {
_hitter = hitter;
}
public int Invoke(SpriteA sprite) {
return _hitter.Hit(sprite);
}
public int Invoke(SpriteB sprite) {
return _hitter.Hit(sprite);
}
}
// Class taken from question, processes the hits
// currently just outputs what hit what...
class HitHelper {
public static void Hit(SpriteA a, SpriteB b) {
Console.WriteLine("a hit b");
}
public static void Hit(SpriteB b, SpriteA a) {
Console.WriteLine("b hit a");
}
public static void Hit(SpriteB b, SpriteB b1) {
Console.WriteLine("b hit b1");
}
public static void Hit(SpriteA a, SpriteA a1) {
Console.WriteLine("a hit a1");
}
}
class Program {
// class for testing two members
class Collision {
public Sprite Hitter { get; set; } // sprite causing collision
public Sprite Receiver { get; set; } // sprite getting hit
}
static void Main(string[] args) {
// Define each type of collision (A->A, A->B, B->A, B->B)
Collision[] collisions = new Collision[] {
new Collision{Hitter=new SpriteA(), Receiver = new SpriteA()} ,
new Collision{Hitter=new SpriteA(), Receiver = new SpriteB()} ,
new Collision{Hitter=new SpriteB(), Receiver = new SpriteA()} ,
new Collision{Hitter=new SpriteB(), Receiver = new SpriteB()} };
// For each scenario, process the collision
foreach (var collision in collisions) {
// Create the appropriate hitter wrapper for the sprite doing the hit
var hitter = collision.Hitter.Invoke(new HitterCreator());
// perform the collision action against the object that has been hit
var result = collision.Receiver.Invoke(new HitActioner(hitter));
}
// Output:
// a hit a1
// a hit b
// b hit a
// b hit b1
}
}