c#如何将泛型类型参数强制转换并传递给内部函数



想象一下我有一个"zoo"移动应用程序。它从在线服务器上获取"动物",并将它们存储在本地数据库中。

Class Animal
Class Bird : Animal
Class Fish : Animal
Class Pelican : Bird
Class Penguin : Bird

因此,我创建了一个类GetAnimalsFromServerService,它具有以下函数。

public Task<Animal[]> GetAnimalsFromServer<T>() where T : Animal, new() {
if (typeof(T) == typeof(Bird)) {
return GetBirdsFromServer<T>();
} else if (typeof (T) == typeof(Fish)){
return GetFishFromServer<T>();
}
}

private Task<Bird[]> GetBirdsFromServer<T>() where T : Bird, new() {
// Bird specific functionality here.
if (typeof(T) == typeof(Pelican)) {
return _serverConnection.GetPelicansFromServer();
} else if (typeof (T) == typeof(Penguin)){
return _serverConnection.GetPenguinsFromServer();
}
}

这不会编译,因为t的类型是"Animal"而不是"Bird"(GetBirdsFromServer需要类型"Bird(。

既然在"if(typeof(T(==typeof(Bird(("检查之后,我知道T是Bird类型的,有没有办法把T塑造成Bird?

编辑:根据要求,以下行未编译

return GetBirdsFromServer<T>();

错误为:

类型"T"不能用作泛型类型或方法"GetBirdsFromServer(("中的类型参数"T"。没有从"T"到"Bird"的隐式引用转换

编辑2:好吧,我想我已经明白为什么这不起作用了。本质上,我最初的问题是"有没有一个我不知道的关键词可以让我做类似的事情:

return GetBirdsFromServer<(T as Bird)>();

但这是不可能的,因为"as"关键字本质上告诉计算机"yo,这个对象是一只鸟,相信我"。然后,如果它不是一只鸟,计算机可能会像"你撒谎了,我会把对象设置为空"。

但在这种情况下,计算机没有考虑对象。所以当你撒谎时,电脑会说"哦,大便,这不是物体,我该怎么办?"。因此,由于孤立的线条并不能保证t是一只鸟,因此没有办法进行某种类型的铸造或等效。

在这种情况下,您可以使用反射来调用具有所需T-"<(T as Bird)>"的GetBirdsFromServer。另外一些问题(IsAssignableFromContinueWith - Cast用于上行(也得到了修复:

public Task<Animal[]> GetAnimalsFromServer<T>() where T : Animal, new()
{
if (typeof(Bird).IsAssignableFrom(typeof(T)))
{
var method = GetType().GetMethod(nameof(GetBirdsFromServer));
var generic = method.MakeGenericMethod(typeof(T));
var result = generic.Invoke(this, new object[] { });
return (result as Task<Bird[]>)
.ContinueWith(x => x.Result.Cast<Animal>().ToArray());
}
//other similar code
}
public Task<Bird[]> GetBirdsFromServer<T>() where T : Bird, new()
{
// Bird specific functionality here.
if (typeof(T) == typeof(Pelican))        
return _serverConnection.GetPelicansFromServer()
.ContinueWith(x => x.Result.Cast<Bird>().ToArray());        
//other similar code
}

用法:

var task = probe.GetAnimalsFromServer<Penguin>();
//type of task.Result.FirstOrDefault() will be Animal
//but actual type will be Penguin

这里有一种方法可以让GetAnimalsFromServer<T>()在编译时工作。

关键是创建一个包含要返回的所有类型的字典,以及它们将类型返回为Task<Animal[]>的方法。

Dictionary<Type, Func<Task<Animal[]>>> _getFromServer = new Dictionary<Type, Func<Task<Animal[]>>>()
{
{ typeof(Pelican), () => _serverConnection.GetPelicansFromServer().ContinueWith(t => t.Result.Cast<Animal>().ToArray()) },
{ typeof(Penguin), () => _serverConnection.GetPenguinsFromServer().ContinueWith(t => t.Result.Cast<Animal>().ToArray()) },
};

然后,编写GetAnimalsFromServer<T>()方法变得非常简单:

public Task<Animal[]> GetAnimalsFromServer<T>() where T : Animal, new()
{
if (_getFromServer.ContainsKey(typeof(T)))
{
return _getFromServer[typeof(T)]();
}
return default(Task<Animal[]>);
}

我用以下代码进行了测试:

void Main()
{
GetAnimalsFromServer<Pelican>();
GetAnimalsFromServer<Penguin>();
}
public class Animal { }
public class Bird : Animal { }
public class Pelican : Bird { }
public class Penguin : Bird { }
public static class _serverConnection
{
public static Task<Pelican[]> GetPelicansFromServer()
{
Console.WriteLine("Got Pelicans");
return Task.Run(() => new Pelican[] { });
}
public static Task<Penguin[]> GetPenguinsFromServer()
{
Console.WriteLine("Got Penguins");
return Task.Run(() => new Penguin[] { });
}
}

当我运行它时,我会在控制台上得到以下内容:

得到了鹈鹕队得到企鹅

尝试时:

return GetBirdsFromServer<T>();

你基本上说你需要从类型为Animal的服务器上获取鸟。但是您在GetBirdsFromServer中将T定义为继承Bird的类。你所做的回报就像动物(T(从伯德那里继承的一样。

你需要做的是制定某种策略/工厂,让你得到你想要的回应。例如:

return GetBirdsFromServer<Pelican>();

另一件值得一提的事情是,在这种情况下,泛型似乎有点太多了(过度设计(。带有多态性的简单继承就足够了。

最新更新