【问题标题】:c# How do I cast and pass a generic type parameter to an internal function?c#如何将泛型类型参数强制转换并传递给内部函数?
【发布时间】:2019-01-09 02:48:22
【问题描述】:

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

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”关键字本质上是在告诉计算机“哟,这个物体是一只鸟,相信我”。然后,如果它不是一只鸟,计算机可能会像“你撒谎了,那我就把对象设置为空”。

但在这种情况下,计算机并未考虑对象。因此,当您撒谎时,计算机就像“哦,便便,这不是对象,我该怎么办?”。因此,由于单独的行并不能保证 T 是 Bird,因此无法进行某种类型的强制转换或等效。

【问题讨论】:

  • 你有一个带有类型参数的抽象类吗?
  • 如果泛型成员只在类型上起作用,那么它是毫无意义的。这是您必须区分类型的时候。事实上,泛型成员不应该知道任何可能匹配泛型约束的可能类型。这就是通用约束的全部意义所在。话虽如此,您不应该使用通用方法,而应为每种可能的类型使用不同的方法。
  • 如果你需要为每种类型做特定的事情,泛型是错误的工具。
  • 您试图在这里混合两个概念 - 泛型和继承。不知道你是否理解。总之,不好。忘记泛型,专注于只使用类层次结构来解决这个问题。
  • 重点是,T 是一种在编译时已知的类型。因此,当您知道编译时的时间时,您也可以只调用另一个方法。如果您使用GetAnimalFromServer&lt;Penguin&gt; 而不是GetPenguinFromServer,则没有任何好处,是吗?无论如何,您都必须提供类型。显然,您想要的是从数据库中检索实体,而无需在编译时完全知道类型。在这种情况下,泛型是错误的工具。

标签: c# generics types parameters


【解决方案1】:

在这种情况下,您可以使用反射来调用 GetBirdsFromServer 所需的 T - "&lt;(T as Bird)&gt;"。还修复了一些其他问题(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

【讨论】:

    【解决方案2】:

    这里有一种方法可以让您的 GetAnimalsFromServer&lt;T&gt;() 在编译时工作。

    关键是创建一个包含您要返回的所有类型的字典及其将类型返回为Task&lt;Animal[]&gt; 的方法。

    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&lt;T&gt;() 方法就变得非常简单了:

    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[] { });
        }
    }
    

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

    有鹈鹕 有企鹅

    【讨论】:

      【解决方案3】:

      当你尝试时:

      return GetBirdsFromServer<T>();
      

      您基本上说您需要从Animal 类型的服务器获取鸟类。但是您将GetBirdsFromServer 中的T 定义为继承Bird 的类。您对返回所做的操作类似于 Animal (T) 继承自 Bird。

      您需要做的是制定某种策略/工厂来获得您想要的响应。例如:

      return GetBirdsFromServer<Pelican>();
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-13
        • 2021-12-04
        • 2022-11-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-19
        相关资源
        最近更新 更多