[英]c# How do I cast and pass a generic type parameter to an internal function?
想象一下,我有一个“动物园”移动应用程序。 它从在线服务器获取“Animals”并将它们存储在本地数据库中。
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”而不是Type“Bird”(而GetBirdsFromServer需要类型为“Bird”)。
因为在“if(typeof(T)== typeof(Bird))”之后我知道T是Bird类型的,有没有办法让我把T作为鸟?
编辑:根据要求,下面的行不编译
return GetBirdsFromServer<T>();
错误是:
类型'T'不能用作泛型类型或方法'GetBirdsFromServer()'中的类型参数'T'。 从'T'到'Bird'没有隐式引用转换
编辑2:好的,我想我已经理解了为什么这不起作用。 基本上我原来的问题是“是否有一个我不知道的关键字允许我做相同的:
return GetBirdsFromServer<(T as Bird)>();
但这是不可能的,因为“as”关键字基本上告诉计算机“哟,这个对象是一只鸟,相信我”。 然后,如果它不是鸟,那么计算机可能就像“你撒谎,我只是将对象设置为null”。
但在这种情况下,计算机不考虑对象。 因此,当你撒谎时,电脑就像“哦,便便,这不是一个对象,我该怎么做?”。 因此,由于孤立的线不能保证T是Bird,所以没有办法进行某种类型的转换或等效。
以下是解决此问题的方法,以使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[] { });
}
}
当我运行它时,我在控制台上得到以下内容:
Got Pelicans Got Penguins
在这种情况下,您可以使用反射来调用带有所需T
“ GetBirdsFromServer
” - “ <(T as Bird)>
”。 另一个问题( IsAssignableFrom
, ContinueWith - Cast
for upcasting)也是固定的:
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
当你尝试:
return GetBirdsFromServer<T>();
你基本上说你需要从Animal
类型的服务器中获取鸟类。 但是你在GetBirdsFromServer
中将T定义为继承Bird
的类。 你对那次回归做了什么就像动物(T)继承自伯德。
您需要做的是制定某种策略/工厂,以获得您想要的响应。 例如:
return GetBirdsFromServer<Pelican>();
另外值得一提的是,在这种情况下,仿制药看起来有点太多了(过度设计)。 具有多态性的简单继承应该足够了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.