我如何强制转换并将类型参数传递给内部函数?

时间:2018-08-01 13:40:56

标签: c# generics types parameters

想象一下我有一个“动物园”移动应用。它从在线服务器上获取“动物”并将其存储在本地数据库中。

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的类型为“动物”,而不是类型为“鸟”(并且GetBirdsFromServer要求类型为“鸟”)。

自从经过“ if(typeof(T)== typeof(Bird))”检查之后,我知道T是Bird类型,是否有办法将T转换为Bird?

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

return GetBirdsFromServer<T>();

错误是:

  

类型'T'不能用作通用类型或方法'GetBirdsFromServer()'中的类型参数'T'。没有从“ T”到“ Bird”的隐式引用转换

编辑2: 好吧,我想我对为什么这行不通了。本质上,我最初的问题是“是否有一个我不知道的关键字,该关键字使我可以执行以下操作:

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

但这是不可能的,因为关键字“ as”实际上告诉计算机“哟,这个对象是鸟,请相信我”。然后,如果它不是鸟,那么计算机可能会像“您撒谎了,然后将对象设置为null”。

但是在这种情况下,计算机没有考虑物体。因此,当您说谎时,计算机就像“哦,大便,这不是对象,我要做什么?”。因此,由于孤立的行不能保证T是Bird,因此无法进行某种类型的强制转换或等效操作。

3 个答案:

答案 0 :(得分:1)

这里有一种解决方法,可以使您的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

答案 1 :(得分:1)

在这种情况下,您可以使用反射来使用所需的publish the build to App Store from website-“ GetBirdsFromServer”来调用T。其他一些问题(<(T as Bird)>IsAssignableFrom用于上播)也已修复:

ContinueWith - 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
}

答案 2 :(得分:0)

尝试时:

return GetBirdsFromServer<T>();

您基本上说您需要从Animal类型的服务器中获取鸟。但是您在GetBirdsFromServer中将T定义为继承Bird的类。您使用该收益所做的就像是Animal(T)继承自Bird一样。

您需要做的是制定某种策略/工厂,以获取所需的响应。例如:

return GetBirdsFromServer<Pelican>();

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