所以我在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应该是子类)。
答案 0 :(得分:1)
两种方法,OP。
可以使用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
关键字,仅允许在界面中使用。
很多人很难获得协方差。您可以通过引入中间类来避免整个问题,摆脱接口并跳过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;
}
}