I'd like to ask something about generics.
I am trying to keep the code simple, and thus I will be making a single class to handle load/save for a game's savegame files. As each portion of the game has different requirements I'd like to keep this as easily accessible as possible:
public void Load<T>(string path, out T obj)
{
BinaryFormatter bf = new BinaryFormatter();
using (FileStream file = File.Open(Application.persistentDataPath + path, FileMode.Open))
{
obj = (T)bf.Deserialize(file);
}
}
Now I can call this with a simple
TurnData x; s.Load("test.txt", out x);
The alternative would be to make the Load function return the object and then convert it to a TurnData type.
TurnData x = (TurnData)s.Load("test.txt");
I do not know much about C#. I assume that the code inside using(...) { ... }
does not get executed if there is an error opening the file for example? If someone can confirm this that would be nice. The example code I have seen did not have any error handling, which seemed weird to me, so I added using?
So in this secondary version where the function returns the object instead of using an out parameter would need more complicated code for error checking and possible return null? It doesn't seem great.
So the real question is ... can I use the next version I have here or are there concerns that I should have due to the use of generics?
There is no generic code bloat for reference types - code is reused. With value types, though, CLR will generate a separate method for each type. See .NET Generics and Code Bloat .
The using
statement has nothing to do with error handling. Using File.Open
method you can expect to get the exceptions you will find here . You could avoid the abruptly stop of your program from any such an exception by wrapping your using statement in a try/cath
construct like below:
public T Load<T>(string path)
{
T obj = default(T);
var bf = new BinaryFormatter();
try
{
using (var file = File.Open(Application.persistentDataPath + path, FileMode.Open))
{
obj = (T)bf.Deserialize(file);
}
}
catch(Exception exception)
{
// Log the exception
}
return obj;
}
Essentially you attempt to Open the file specified in the path. If that fails you just log the failure and your return null
from the function.
Regarding the using
statement, it provides
a convenient syntax that ensures the correct use of IDisposable objects.
as you can read more thoroughly here
As a side note regarding the signature of your method I would make a few comments. Consider the following method body and spot the differences with that we have above.
public T Load<T>(string path, IFormatter formatter)
{
if(path ==null) throw new ArgumentNullException(nameof(path));
if(formatter == null) throw new ArgumentNullException(nameof(formatter));
T obj = default(T);
try
{
using (var file = File.Open(path, FileMode.Open))
{
obj = (T)formatter.Deserialize(file);
}
}
catch(Exception exception)
{
// Log the exception
}
return obj;
}
and
var path = Path.Combine(Application.persistentDataPath, "test.txt");
var binaryFormatter = new BinaryFormatter();
var x = s.Load(path, binaryFormatter);
Making the above changes you make your method more easily to be tested through a unit test and more reliable since you make some precondition checking before the meat and potatoes of your method. What would had happened if you had passed a null path
? What would had happened if you had passed a null formatter ? etc...
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.