c#自己类的泛型类型

时间:2018-01-20 01:52:27

标签: c# generics

所以我在c#中遇到泛型类型/继承问题,这是代码

   public class Tile{
       ...
       public TileMap<Tile> tileMap{ get; set; }
       ...
   }

   public class TileMap<T> where T : Tile{
       ...
       T newTile = (T)Activator.CreateInstance(typeof(T));
       newTile.tileMap = this; //no compile
       ...
   }

编译器说:cannot convert TileMap<T> to TileMap<Tile>

我可以在Tile中创建一个泛型类型,我总是提供与tile本身相同的类型,但这对我来说似乎并不好(并且可能出错)。

我认为我需要一种自动使用自身(最大的子类)的通用类型

(TileMap中有Tile的子类,可以在TileMap中使用)

这是我想要的想法:

     public class Tile<T> where T : this{
         ...
         public TileMap<T> tileMap { get; set; }
         ...
      }

但是不必初始化泛型类型,因为它应该始终与类相同(如果我使用Tile的子类,T应该是子类)。

2 个答案:

答案 0 :(得分:1)

两种方法,OP。

1。正确的方式&#34; (使用协方差)

可以使用covariance完成,这很难解释,但很容易显示。协方差要求您使用接口。这将编译和工作:

public interface ITileMap<out T> where T : Tile
{
}

public class Tile
{
    public ITileMap<Tile> tileMap { get; set; }
}

public class TileMap<T> : ITileMap<T> where T : Tile, new()
{
    public TileMap()
    {
        T newTile = new T();
        newTile.tileMap = this;   //This compiles now!!! 
    }

    public override string ToString()
    {
        return "Hello world!!!";
    }
}

public class Program
{
    public static void Main()
    {
        var t = new TileMap<Tile>();
        Console.WriteLine(t);
    }
}

输出:

Hello world!!!

Code available on DotNetFiddle

关键是这一行:

public interface ITileMap<out T> where T : Tile

out关键字告诉c#ITileMap<base>类可以引用ITileMap<derived>接口。使用泛型,你必须明确这一点。

虽然常规继承允许您分配base x = derived,但c#不允许TileMap<base> x = TileMap<derived>。为什么?他们不会相互继承。相反,他们&#34;继承&#34; (不是真的)来自其他东西,一个共同的祖先,TileMap<>,这是他们的generic type definition。类型定义不能用作类型,因此我们在泛型世界中处理这类事情的方式是 variance 。在这种特定情况下,您需要协方差,例如out关键字,仅允许在界面中使用。

2。简单的方法

很多人很难获得协方差。您可以通过引入中间类来避免整个问题,摆脱接口并跳过out关键字,如下所示:

public class TileOnlyMap : TileMap<Tile>

现在您的代码将编译:

public class TileOnlyMap : TileMap<Tile>
{
    public TileOnlyMap()
    {
        Tile newTile = new Tile();
        newTile.tileMap = this;
    }
}

在这个例子中,我们定义了一个新类TileOnlyMap,它本身不接受任何泛型参数,但是继承自TileMap<Tile>,其中泛型类型参数在编译时确定。当你这样做时,你失去的主要是早期类型绑定。您仍然可以使用TileOnlyMap来包含Tile的子类,现在它们将与运行时多态性一起存储,而不是通用类型兼容性和方差(此成本可以忽略不计)。例如:

public class SpecificTypeOfTile : Tile
{
}

public class TileOnlyMap : TileMap<Tile>
{
    public TileOnlyMap()
    {
        var newTile = new Tile();
        newTile.tileMap = this;

        var newTile2 = new SpecificTypeOfTile();  //This works because of polymorphism
        newTile2.tileMap = this;
    }
}

See the full, second example on DotNetFiddle

就个人而言,除非你仔细规划了你的通用对象模型,否则我会坚持使用更为简单的第二个例子。如果你的方差/接口/对象模型错了,你真的会造成严重的混乱!另一方面,如果您了解共同/逆转并计划出通用对象方案,则第一个答案是首选方法。而且我保证你工作的越多就越有意义。

答案 1 :(得分:1)

如果你可以为TileMap创建一个接口,它只使用T作为回报,那么你可以利用协方差:

public interface ITileMap<out T> where T : Tile
{
    T DoSomething();

    //this solution won't work if you need something like this:
    //void DoSomethingElse(T tile);
}

然后修改Tile属性以取代接口:

public class Tile
{
    public ITileMap<Tile> tileMap{ get; set; }
}

然后是TileMap的实现:

public class TileMap<T> : ITileMap<T> where T : Tile
{

    public T DoSomething()
    {
        T newTile = (T)Activator.CreateInstance(typeof(T));
        newTile.tileMap = this;
        return newTile;
    }
}