I am currently trying to save and load data for multiple classes to/from disk. I am using JSON serialization to read and write a text file with a custom file extension. This works perfectly fine.
However, instead of duplicating the code in every class I want to use inheritance and only have the save/load code once in an abstract superclass. I want to have a different file extension for each class.
Saving is not a problem, because I have an object, so I can simply use an abstract property, but when I want to load the data I don't, so I can't get the property value without first creating an instance of the type I want to load, which I find stupid.
Also, I can't make abstract static/const properties/fields in C# (for stupid reasons, don't even start), and I am out of good ideas.
//this is fine, although I don't really need the new constraint, but I keep it for consistency with the Load function
public static void Save<T>(string filename, T obj) where T : JsonSerializeable, new() {
if (filename.IsNullOrWhiteSpace()) filename = $"{DEFAULT_FILENAME}";
string path = Path.ChangeExtension($"{DIRECTORY_NAME}/{filename}", obj.fileExtension);
path = Path.ChangeExtension(path, obj.fileExtension);
if (!Directory.Exists(DIRECTORY_NAME)) Directory.CreateDirectory(DIRECTORY_NAME);
File.WriteAllText(path, JsonConvert.SerializeObject(obj, Formatting.None));
}
//this works, but I hate that I need to create an instance that has no other use than getting the property value
public static bool Load<T>(string filename, out T obj) where T : JsonSerializeable, new() {
if (filename.IsNullOrWhiteSpace() || !Directory.Exists(DIRECTORY_NAME)) {
obj = default;
return false;
}
string fileExtension = new T().fileExtension; //TODO -.-
string path = Path.ChangeExtension($"{DIRECTORY_NAME}/{filename}", fileExtension);
if (!File.Exists(path)) {
obj = default;
return false;
}
obj = JsonConvert.DeserializeObject<T>(File.ReadAllText(path));
return true;
}
Alternatively, I could have public save/load methods in the subclasses, with the desired interface (i.e. without the extension) and have the superclass methods protected and there pass the extension as parameter. However, I still don't really like that. It is acceptable, and my current implementation (because creating a useless object doesn't fly), but is there a good way to do it with only the superclass functions? (and no, I don't want to use the class name as the file extension)
Note: As of now this is more of an academic question, since I have a working implementation, but this was only the latest instance of that same problem popping up in my programming over and over again, and I always find some kind of workaround. I would love to finally have a good solution.
CodePudding user response:
There is no way to achive that without instantiation. Only static members are part of the class and could be retrieved like this. But there is no way you could override, inherit or provide a contract for that, which I guess you'd want.
I could be wrong, but it seems you'd like to decorate classes with some extra info (file extension). So, you could do that easily with attributes.
I've done a sample for how it would look using Attributes.
https://dotnetfiddle.net/deQ4zH
Let me know your thoughts.
CodePudding user response:
One way to do this would be to create an abstract class with a static member for the Extension
property, and then hide that property in the child class implementations:
public abstract class FileType
{
public static string Extension { get; } = "default";
}
public class PDFFile : FileType
{
public static new string Extension { get; } = "pdf";
}
public class TextFile : FileType
{
public static new string Extension { get; } = "txt";
}