Here’s a proposed refinement of HanClinto’s response:
MyScriptableObject someInstance = MyScriptableObject.Create(
new MyScriptableObjectConfig(){
level = 5,
name = "Gorlock",
age = 15,
map = owningMap
}
);
Here, the Create function is a public static shortcut function like this:
public static MyScriptableObject Create(MyScriptableObjectConfig config){
return MyScriptableObject.CreateInstance<MyScriptableObject>().Init(config);
}
public MyScriptableObject Init(MyScriptableObjectConfig config){
..
return this;
}
This way of doing it has some advantages:
- the creation parameters are named, so we don’t have to rely on intelisenseless to remind us of what they are
- support for more (optional) creation parameters can be added for other possible use cases without changing this code at all
- only one Init function and one Create function is needed, regardless of how many optional initialization parameters are eventually supported
- the properties of the config object can be types that have explicit or implicit conversions defined for the various potential use cases, allowing the initialization code and the consumption code (above) to be free of having to deal with those kinds of concerns
Note that this code assumes that if CreateInstance() didn’t produce an instance, some sort of exception was (or should be) raised, rather than null being passed back. If the failure of the creation of the instance is a possibility that shouldn’t be an exception, then this code will need to be modified. (But so would HanClinto’s, despite its use of ‘as’.)
If the object isn’t intended to be involved in the effort to persist things (and therefore doesn’t need to support serialization) this alternative could be used:
MyObject someInstance = new MyObject(new MyObjectConfig(){
.
.
});
… thereby supporting the use of readonly properties (that can only be set during construction) without fear of ending up proliferating constructor parameters, constructor overloads, etc. If MyObject derives from ScriptableObject, you’ll get a warning about using the constructor, probably because of the serialization issue, but it doesn’t seem to cause any problem if you never serialize or deserialize.
By the way, you can create objects of your own classes that don’t derive from ScriptableObject at all. All you have to do is put them in a file of the same name as the class and it’ll be globally accessible. (The class’s config class will be accessible in any file that you use the class in.)
The definition for the config struct or class can be right above the definition for the object class. It can be as simple as a list of public variables, or as complex as needed to support whatever default values, parameter conversions, et etc. might eventually be desired.
The Create function can be put into a base class, the config object parameter to it can be made nullable, so then, in the case where the defaults are acceptable…
MyScriptableObject someInstance = MyScriptableObject.Create(null);
or
MyObject someInstance = new MyObject(null);
… if the default parameters are obvious enough and serialization is not required. And/or if you feel like it, you can create a single default constructor that passes a default constructed MyObjectConfig to the main constructor. Then you can do this:
MyObject someInstance = new MyObject();
and still never have to worry about ending up with a mass of constructor overloads and explicit constructor parameters.
There are a lot of ways to attack the beast in C#. Not every class is concerned with serialization, in fact separating out the concern of persisting things is a good practice. Passing parameters to constructors can result in less code to create and maintain, and it doesn’t have to end up causing problems. Just don’t let yourself end up with a ton of constructor parameters or overloaded constructors; it’s easy enough to avoid. And don’t pass parameters to constructors that are going to end up needing to be serialized/deserialized. You don’t serialize things like file handles or database connections or request contexts. Under particular circumstances, it may be ok to pass some things like that as optional constructor parameters even to objects that need to support serialization, if they have defaults that can be used in situations where serialization is going on.