I am pulling my hair out on this one. It seems so simple but it's not. I just want to create a simple list of settings. Each setting has a "name", "value" and "default". Each setting's "value"/"default" can be any simple type (bool, int, float, string, etc).
It seems the list needs to be iterable so I can implement read/write functions to save the settings and load them later, or search for a setting by name to determine if it exists, return its value, etc.
Here is what I tried (among lots of other things):
abstract class BaseSetting
{
public string Name { get; set; }
}
class Setting<T> : BaseSetting
{
public Setting(string name, T value, T def) { Name = name; Value = value; DefaultValue = def; }
public T Value { get; set; }
public T DefaultValue { get; set; }
}
// Create iterable list of settings:
List<BaseSetting> Settings = new List<BaseSetting>(); // Have to use the Base class so the list will accept subclasses with various types.
// Add subclasses of various types to the list:
Settings.Add(new Setting<int>("IntSetting", 123, 456));
Settings.Add(new Setting<bool>("BoolSetting", true, false));
Settings.Add(new Setting<double>("FloatSetting", 3.14, 2.718));
// Iterate settings to do something (i.e. save all settings to a file)
foreach(var s in Settings)
{
Console.WriteLine("Name of setting: " s.Name);
Console.WriteLine("Value of setting: " s.Value.ToString()); // ERROR: no member named "Value"
}
(See fiddle: https://dotnetfiddle.net/F4x2Di)
In attempting to iterate the settings, I can't access the Value or DefaultValue properties, since they're not present in the base class, and I can't attempt to cast the base class to a subclass, because I can't access the Value/DefaultValue properties to get their types to pass to the cast.
I also thought I could add a generic "object" property in the base class for Value and DefaultValue, then override it with the desired type in the subclass, but that's not allowed either: can't change type in overrides.
My remaining hair would greatly appreciate guidance on this one. Is there a way to implement such a class with generic types in an iterable list? or is this the wrong approach to the problem at hand?
CodePudding user response:
Well, thats a nice one. If you would access it in the baseclass, the only type it could have is object (unless you restrict T)
So: https://dotnetfiddle.net/lHE1gv
using System;
using System.Collections.Generic;
abstract class BaseSetting
{
protected abstract object GetValue();
public string Name { get; set; }
public object Value{
get
{
return GetValue();
}
}
}
class Setting<T> : BaseSetting
{
public Setting(string name, T value, T def) { Name = name; Value = value; DefaultValue = def; }
public new T Value { get; set; }
public T DefaultValue { get; set; }
protected override object GetValue()
{
return Value;
}
}
public class Program
{
public static void Main()
{
// Create iterable list of settings:
List<BaseSetting> Settings = new List<BaseSetting>();
Settings.Add(new Setting<int>("IntSetting", 123, 456));
Settings.Add(new Setting<bool>("BoolSetting", true, false));
Settings.Add(new Setting<double>("FloatSetting", 3.14, 2.718));
// Iterate settings to do something (i.e. save all settings to a file)
foreach(var s in Settings)
{
Console.WriteLine("Name of setting: " s.Name);
Console.WriteLine("Value of setting: " s.Value.ToString()); // ERROR: no member named "Value"
}
}
}
CodePudding user response:
Because you base class didn't have a Value property, We can try to use abstract
property to let the subclass override it or use generics for BaseSetting
abstract class BaseSetting
{
public string Name { get; set; }
public abstract object Value { get; set; }
}
class Setting<T> : BaseSetting
{
public Setting(string name, T value, T def) { Name = name; Value = value; DefaultValue = def; }
public override object Value { get; set; }
public T DefaultValue { get; set; }
}
If you don't want to use override
another way we can try to use new
and set the value base.Value
in the constructor function
abstract class BaseSetting
{
public string Name { get; set; }
public object Value { get; set; }
}
class Setting<T> : BaseSetting
{
public Setting(string name, T value, T def) {
Name = name;
base.Value = value;
DefaultValue = def;
}
public new T Value {
get{
return (T)base.Value;
}
set{
base.Value = value;
}
}
public T DefaultValue { get; set; }
}
CodePudding user response:
Your List
is of type BaseSetting
. It does not contain those properties.
The iterated Settings<T>
are extracted as BaseSetting
.
To make use of the Value
etc, you need to cast s
to the appropriate type - for which you'll need to test for first, this can be done through is
, but that is often an indication the solution is flawed:
if (s is Setting<int>)
//Cast and do stuff with int
Because this is not very helpful, and you often know how to handle settings - they are well defined - setting implementations often just use strings or well defined JSON objects.
Alternatively, you can define a bunch of extension methods, or base your class on object
but in both cases you will not gain more than just using a string
as Value
, and cast appropriate when you need it.
De facto this is also how it would work when desirialize into a class using JSON - but in that case you must know your setting names upfront.