Home > Back-end >  C# create iterable list of settings with different data types
C# create iterable list of settings with different data types

Time:04-29

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

c# online

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.

  • Related