【问题标题】:Return type T can't be returned as null? C# Generics返回类型 T 不能返回为 null 吗? C# 泛型
【发布时间】:2011-09-07 20:50:24
【问题描述】:

我有一个方法可以从用户提供的文件路径和对象类型中反序列化存储的对象。该方法工作正常,除非用户提供了无效的文件路径。在这种情况下,我希望我的方法返回 null,但是当我尝试返回 null 时,会出现编译错误。我尝试使用可为空的类型,但出现编译错误。相反,我对一个对象进行类型转换并返回它,但这会导致运行时错误。我想知道是否有人知道允许返回 null 的正确方法。代码如下:

        public static T RestoreObj<T>(string datafile)
    {


        try
        {
            var fs = File.OpenRead(datafile);
            var bf = new BinaryFormatter();
            var obj = (T) bf.Deserialize(fs);
            fs.Close();
            return obj;
        }
        catch (Exception e)
        {
            MessageBox.Show("Could not load. Accepts valid *.dom files only. " + e);

            // TODO: how to do this? this will throw a runtime error, and if null returned, a compilation error
            var o = new object();
            return (T) o;
        }
    }

在考虑了 Eric Lippert 的质量指标后,我修改了该方法,使其看起来像您在下面看到的那样。使用 'using' 的优点是它会自动生成一个 try..finally 块,该块将调用 dispose 方法(FileStream 实现 IDisposable,如果没有,它们将是编译错误)。另一件好事是抛出的异常与实际发生的事情有关,而不是我上面所说的。

        public static T RestoreObj<T>(string datafile) 
    {
        using (var fs = File.OpenRead(datafile))
        {
            var bf = new BinaryFormatter();
            var obj = (T)bf.Deserialize(fs);
            return obj;
        }
    }

【问题讨论】:

    标签: c# generics nullable


    【解决方案1】:

    如果您只打算使用类,请添加 where T : class 约束:

    public static T RestoreObj<T>(string datafile) where T : class
    

    如果您也希望反序列化结构,那么只需返回 default(T)。引用类型为null,结构的默认值(通常为0)。正如@JMH 指出的那样,default(Nullable&lt;T&gt;) 是一个包含null 的可空值。

    【讨论】:

    • 也适用于 Nullable 包装的值类型,它们将是一个“null”可为空的值 - 这当然是您的答案所暗示的,但只是明确说明 OP,因为他在他的问题。
    • @James 好点。我会在答案中明确说明,谢谢。
    【解决方案2】:

    我会通过不首先编写该代码来解决问题。

    一个方法应该做一件事并且做好;您将反序列化代码与错误报告代码混在一起。

    不要那样做。更好的方法是让反序列化方法抛出异常,并编写处理异常并向用户报告错误的不同代码。

    更一般地说,拥有一个吃掉异常然后返回虚假数据的方法是很危险的。当毫无戒心的代码调用您的方法期望得到良好的数据时,这只会产生问题。

    虽然我们讨论的是代码质量,但您应该使用“使用”块来确保在发生异常时关闭文件句柄。不要显式地执行 fs.Close() -- 而是执行 using(var fs = ... ) 并让编译器生成关闭文件的处置。

    【讨论】:

    • 这是很好的反馈,任何能提高我的代码质量的东西我都会像海绵一样吸收。我将重写我的方法,看看我是否可以实施您的建议。我知道一个方法应该做一件事并且做得很好,但我从来没有理解如何将错误处理分离到不同的代码中。你的意思是客户端应该处理错误捕获吗?
    • 你觉得我的编辑怎么样?我应该知道的任何其他质量改进?
    【解决方案3】:

    您可以使用default(T) 代替null,null 用于引用类型和默认值用于值类型。

    【讨论】:

      【解决方案4】:

      并非所有类型都可以设置为null。

      你必须约束T:

      public static T RestoreObj<T>(string datafile) where T : class
      

      您的另一个选择(如果您不严格使用类)是返回 default(T) 而不是 null。

      【讨论】:

        【解决方案5】:

        如果你这样声明你的方法,你应该可以返回null:

        public static T RestoreObj<T>(string datafile) : where T class
        

        T 也可以是一个结构体,它不能是null。如果您将T 限制为类,则它必须是引用类型,并且您可以返回null。

        【讨论】:

          【解决方案6】:

          将T 约束为:

           public static T RestoreObj<T>(string datafile) where T : class
                                                         //^^^^^^^^^^^^^^
          

          这意味着您可以使用T 调用此方法,这是一个引用类型,可以是null。当T是值类型时不能调用这个方法,但是当T是Nullable&lt;V&gt;时调用它:

          class  R {} //Reference type!
          struct V {} //Value type!
          
           Xyz.RestoreObj<R>("abc"); //ok
           Xyz.RestoreObj<V>("abc"); //compilation error
           Xyz.RestoreObj<Nullable<V>>("abc"); //ok
          

          【讨论】:

            【解决方案7】:

            您可以使用以下内容来检查文件是否存在并返回类型的默认值。

                        if (!File.Exists(FilePath))
                        return default(T);
            

            通过使用它,您将获得该类型的默认值。例如,如果你给它一个“int”类型,它将返回 0 而不是 null。

            【讨论】:

            • 很好,我不知道默认是什么,谢谢你的解释。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-10-18
            • 1970-01-01
            • 1970-01-01
            • 2014-05-16
            相关资源
            最近更新 更多