简体   繁体   English

返回类型T不能返回null? C#泛型

[英]Return type T can't be returned as null? C# Generics

I have a method that generically deserializes a stored object from a users provided filepath and object type. 我有一个方法,通常从用户提供的文件路径和对象类型反序列化存储的对象。 The method works fine, except for when the user provides an invalid filepath. 该方法工作正常,但用户提供无效文件路径时除外。 I would like my method to return a null in this case, but when I attempt to return null I get a compilation error. 我希望我的方法在这种情况下返回null,但是当我尝试返回null时,我得到一个编译错误。 I tried to use a nullable type but get a compilation error. 我试图使用可空类型,但得到编译错误。 Instead, I typecast an object and return that, but it causes a runtime error. 相反,我对一个对象进行类型转换并返回该对象,但它会导致运行时错误。 I would like to know if anyone knows the proper way to allow for returning a null. 我想知道是否有人知道允许返回null的正确方法。 The code is as follows: 代码如下:

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

After taking Eric Lippert's quality comments in to consideration I revised the method to look like what you see below. 在考虑了Eric Lippert的质量评论后,我修改了方法,看起来就像你在下面看到的那样。 The advantage of using 'using' is that it automatically generates a try..finally block that will call the dispose method (FileStream implements IDisposable, if it did not their would be a compile error). 使用'using'的优点是它会自动生成一个try..finally块,它将调用dispose方法(FileStream实现IDisposable,如果不是它们将是一个编译错误)。 Another nice thing is that the exception thrown is relevant to whatever is actually happening instead of what I have above. 另一件好事是抛出的异常与实际发生的事情有关,而不是我上面所做的。

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

If you're only going to work with classes, then add the where T : class constraint: 如果你只是要使用类,那么添加where T : class约束:

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

If you expect to deserialize structs as well, then just return default(T) . 如果您希望反序列化结构,则只返回default(T) That will be null for reference types, and the default value (usually 0) for structs. 对于引用类型,它将为null ,并且结构的默认值(通常为0)。 As @JMH points out, default(Nullable<T>) is a null -containing nullable. 正如@JMH指出的那样, default(Nullable<T>)是一个包含null的可空null

I would solve the problem by not writing that code in the first place. 我不会首先编写代码来解决问题。

A method should do one thing and do it well; 一种方法应该做一件事,做得好; you are mixing up deserialization code with error reporting code. 您正在将反序列化代码与错误报告代码混合在一起。

Don't do that. 不要那样做。 A better way would be to have the deserialization method throw an exception, and write different code that handles the exceptions and reports errors to the user. 更好的方法是让反序列化方法抛出异常,并编写处理异常的不同代码并向用户报告错误。

More generally, it is dangerous to have a method that eats exceptions and then returns bogus data. 更一般地说,拥有一个吃异常然后返回伪造数据的方法是危险的。 That is just creating problems down the line when unsuspecting code that calls your method expects to get good data back. 这只是在不知情的调用您的方法的代码期望获得良好数据时产生问题。

While we're on the subject of code quality, you should be using "using" blocks to ensure that the file handles are closed if an exception happens. 虽然我们讨论代码质量问题,但您应该使用“using”块来确保在发生异常时关闭文件句柄。 Do not explicitly do a fs.Close() -- rather, do using(var fs = ... ) and let the compiler generate the disposal that closes the file. 不要显式地执行fs.Close() - 而是using(var fs = ... )并让编译器生成关闭文件的处理。

您可以使用default(T)而不是null ,对于引用类型和值类型的默认值,它将为null

Not all types can be set to null . 并非所有类型都可以设置为null

You have to constrain T : 你必须约束T

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

Your other option (if you're not strictly working with classes) is to return default(T) rather than null. 你的另一个选择(如果你没有严格使用类)是返回default(T)而不是null。

Put constraint on T as: 将约束放在T上:

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

This mean that you can call this method with T which is a reference type which can be null . 这意味着您可以使用T调用此方法,这是一个可以为null的引用类型。 You cannot call this method when T is a value type, though you call it when T is Nullable<V> : T是值类型时,您不能调用此方法,但是当T为Nullable<V>时调用它:

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

If you declare your method like this, you should be able to return null : 如果你这样声明你的方法,你应该能够返回null

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

T could also be a struct, which cannot be null . T也可以是一个结构,不能为null If you restrict T to be a class, it has to be a reference type and you are allowed to return null . 如果将T限制为类,则它必须是引用类型,并且允许返回null

You can use the following to check to see if the file exists and return the default for the type. 您可以使用以下内容检查文件是否存在并返回该类型的默认值。

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

By using this you will get the default for the type. 通过使用它,您将获得该类型的默认值。 For example, if you give it a type of 'int' it will return 0 instead of null. 例如,如果给它一个'int'类型,它将返回0而不是null。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM