What pattern should I use to express a Hierarchical Enum? - c#

I'm experimenting with an API for publishing values at a given time (tuples of value and time). These samples will be used by a data viewer (e.g. a graph).
I want to associate the value with a Quantity and a Unit, for example length in meters. That way my "viewer" can scale it appropriately.
I'm looking for a sort of hierarchical enum, like this:
enum Quantity
{
Mass.Kg,
Mass.g,
Length.m,
Length.mm
}
But this doesn't exist in C#.
I'm not sure the best pattern to express this and I've come up with the following. Is there a recognised, or better way to do this?
using System;
using Moq;
namespace ConsoleApplication26
{
class Program
{
static void Main(string[] args)
{
//use a Mock to play with the API
Mock<ITelemetryPublisherFactory> mockTelemetryPublisherFactory = new Mock<ITelemetryPublisherFactory>();
var telemetryPublisherFactory = mockTelemetryPublisherFactory.Object;
//example usages
var massTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Double>("My Mass", Mass.Kg);
massTelemetryPublisher.PublishChannelSampleAtTimeNow(83.4);
var lengthTelemetryPublisher = telemetryPublisherFactory.GetChannelSamplePublisher<Int32>("My Height", Length.μm);
lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);
//10 years time..
lengthTelemetryPublisher.PublishChannelSampleAtTimeNow(1800000);
massTelemetryPublisher.PublishChannelSampleAtTimeNow(120.1);
}
}
public interface ITelemetryPublisherFactory
{
ITelemetryPublisher<T> GetChannelSamplePublisher<T>(String channelName, Quantity quantity);
}
public interface ITelemetryPublisher<T>
{
void PublishChannelSampleAtTimeNow(T sampleValue);
}
public abstract class Quantity {}
public class Mass : Quantity
{
private enum Unit
{
g,
Kg
}
private readonly Unit _unit;
private Mass(Unit unit)
{
_unit = unit;
}
public static Quantity Kg {get { return new Mass(Unit.Kg); }}
public static Quantity g { get { return new Mass(Unit.g); } }
public override string ToString()
{
return String.Format("Mass.{0}", _unit);
}
}
public class Length : Quantity
{
private enum Unit
{
m,
mm,
μm,
beardSecond
}
private readonly Unit _unit;
private Length(Unit unit)
{
_unit = unit;
}
public static Quantity m { get { return new Length(Unit.m); } }
public static Quantity mm { get { return new Length(Unit.mm); } }
public static Quantity μm { get { return new Length(Unit.μm); } }
public static Quantity beardSecond { get { return new Length(Unit.beardSecond); } }
public override string ToString()
{
return String.Format("Length.{0}", _unit);
}
}
}

I think it's better to create a Unit class for the unit of measure and a Quantity class that associates a unit of measure with an amount. Look at the Quantity pattern for the idea. Since you also want to record the "type" of the unit of measure, you could create a UnitType class that records that information:
public sealed partial class UnitType {
public string Name { get; private set; }
public UnitType(string name) {
Name = name;
}
}
public sealed partial class Unit {
public string Name { get; private set; }
public UnitType Type { get; private set; }
public Unit(string name, UnitType type) {
Name = name;
Type = type;
}
}
(You should make them proper value types by overriding Equals and GetHashCode)
The Unit class can be extended to provide for e.g. conversions, compound units, formatting and parsing.
Then, you can define the common cases inside the classes:
public partial class UnitType {
public static readonly UnitType Mass = new UnitType("Mass");
public static readonly UnitType Length = new UnitType("Length");
}
public partial class Unit {
public static readonly Unit Grams = new Unit("g", UnitType.Mass);
public static readonly Unit Kilos = new Unit("kg", UnitType.Mass);
// ...
}
Or define your "hierarchies" with static classes:
public static class Mass {
public static readonly UnitType Type = new UnitType("Mass");
public static readonly Unit Grams = new Unit("g", Type);
public static readonly Unit Kilos = new Unit("kg", Type);
...
}
public static class Length ...
The Quantity class would also be an immutable value type (just showing its usage):
var eniacWeight = new Quantity(27, Mass.Tons);
Or you could use extension methods to create Quantitys:
var eniacWeight = 27.Tons();
(from ENIAC)

This is not possible. Enums are primitive types and cannot inherit from other enums, as inheritance is a property of objects.

Hierarchical enum isn't possible, as noted above. If you're exclusively using metric, though, you can utilise standard prefixes if it helps.
enum MeasurementUnits
{
Gram,
Metre,
Litre,
Hectare
// etc
}
enum MeasurementPrefix
{
Milli,
Natural,
Kilo,
Mega
// etc
}
This may not be precisely what you want, but it will provide the type of 'grouping' that you might be looking for (e.g. group measurements that are about length, weight etc by checking their 'units' value).

Your suggested approach seems reasonable to me, and I use something similar in a project of mine. However, I keep the actual value part of the object, and I use struct instead of class, since they are naturally value types. Inheritance is not necessary here (and not possible with structs, anyways), so I use an interface to create a contract and act as a constraint when needed (I called it IUnitOfMeasure).
I do not recommend creating one enum with all the units of the various types of measurement combined; it is hell validating the unit to make sure someone didn't reference a Mass unit when working with Length.
public interface IUnitOfMeasure<TThis>
where TThis : IUnitOfMeasure<TThis>
{
TThis ConvertTo(TThis value);
}
public struct Mass : IUnitOfMeasure<Mass>
{
public enum Units
{
Gram,
Kilogram
}
private double _value;
private Mass.Units _unit;
public double Value { get { return _value; } }
public Mass.Units Unit { get { return _unit; } }
public Mass(double value, Mass.Units unit)
{
_value = value;
_unit = unit;
}
public Mass ConvertTo(Mass value)
{
switch(value.Unit)
{
case Units.Gram:
return new Mass(Unit == Units.Gram ? Value : Value/1000, Units.Gram);
case Units.Kilogram:
return new Mass(Unit == Units.Gram ? Value*1000 : Value, Units.Kilogram);
default:
throw new NotImplementedException();
}
}
public override string ToString()
{
return string.Format("{0} {1}", Value, Unit);
}
public static readonly Mass G = new Mass(0, Units.Gram);
public static readonly Mass Kg = new Mass(0, Units.Kilogram);
}
Usage:
var kg = new Mass(5.0, Mass.Units.Kilogram);
Console.WriteLine(kg); // writes "5 Kilogram"
var g = kg.ConvertTo(Mass.G);
Console.WriteLine(g); // writes ".005 Gram"
If you don't care about keeping the value, and just want to keep enum/static values in a central place:
public static class UnitOfMeasure
{
public enum Mass
{
Gram,
Kilogram
}
public enum Length
{
Meter,
Kilometer
}
// etc.
}
Usage: var unit = UnitOfMeasure.Mass.Kilogram;

You cannot introduce inheritance with enums. Enums are just a convenience mechanism to allow you to use meaningful textual identifiers in your code. From The code you have, I suggest you either use an enum like;
public enum UnitOfMeasure
{
MassGrams,
MassKg,
LengthMM,
LengthCM,
. . .
}
Or split it out to where it's appropriate, so that Mass and Length are defined separately for example.
The 'inheritance' is just something you've introduced in your thinking about this problem, but it isn't necessary to your solution. When you want to deal with Mass, you only look at the flags/enums appropriate to mass.

Related

How to make a class factory to enables only certain properties of a object produced

There's a factory PizzaMaker.GetPizzaObject(PizzaNames pizzaName) which accepts an enum name of one of the pizzas and returns a Pizza object, which stores the amount of each ingredient required in grams:
public class Pizza
{
//the list of all possible properties of a class. Some class exemplars should have some of properties to be disabled.
public int cheese { get; set; }
public int ham { get; set; }
public int pepperoni { get; set; }
}
Since not every type of pizza contains every type of ingredient I want my factory to enable only certain properties in the Pizza object, while the rest should be unavailablefor outside code (for any code for that matter).
My attempt at solution. General C&C welcome:
namespace Testing
{
class Program
{
static void Main(string[] args)
{
PizzaMaker pizzamaker = new PizzaMaker();
Pizza newPizza = pizzamaker.GetPizzaObject(PizzaMaker.PizzaNames.HamAndCheese);
newPizza.cheese = 100;
newPizza.ham = 80;
newPizza.pepperoni = 100; // should throw an error, HamAndCheese shouldn't have "Pepperoni" available.
}
}
public class PizzaMaker
{
//Enum containing all pizza types, used as an argument in PizzaMaker.GetPizzaObject factory
public enum PizzaNames
{
pepperoniPizza,
FourCheese,
HamAndCheese
}
// class factory
public Pizza GetPizzaObject(PizzaNames pizzaName)
{
Pizza result = pizzaName switch
{
PizzaNames.pepperoniPizza => CreatePizza(false, true), //pepperoni has no ham property, has cheese property
PizzaNames.FourCheese => CreatePizza(false, false), //pizza four cheeses has neither ham, nor pepperoni
PizzaNames.HamAndCheese => CreatePizza(true, false), //ham and cheese pizza has ham, but not pepperoni
};
return result;
}
private Pizza CreatePizza(bool hasHam, bool haspepperoni)
{
Pizza result = new Pizza();
hasHam = true ? result.ham.DoSomething(); // somehow enable or disable the property based on wheither it has Ham, pepperoni etc.
haspepperoni = true ? result.pepperoni.DoSomething; // same
// some initialization code, optional
return result;
}
}
public class Pizza
{
//the list of all possible properties of a class. Some class exemplars should have some of properties to be disabled.
public int cheese { get; set; }
public int ham { get; set; }
public int pepperoni { get; set; }
}
}
P.S. the .DoSomething() method is a placeholder for solution, because I suppose something should be done in that place in the code, but I don't know exactly what to do.
I think you are tackling this problem in the wrong way, it's better and more maintainable and less error-prone to have a BasePizza class that contains common properties, then you create a derived class for each of your enum types where you put only properties specific to that Pizza Type.
public class BasePizza
{
// Common properties, like pizza type/name and size
public PizzaNames Name{ get; set; }
// public PizaSize Size { get; set; }
}
public class HamAndCheesePiza: BasePiza
{
public int Ham { get; set; }
public int Cheeze{ get; set; }
}
public class PepperoniPiza: BasePiza
{
public int Pepperoni { get; set; }
}
public class FourCheese: BasePiza
{
// foorcheese related properties
}
next, you want your factory to return the BasePizza class, and in the caller's code you handle the type of the pizza accordingly. if you're using c# 7.0 or higher, the pattern matching using the is will make life easier for you: expr is type varname
static void Main(string[] args)
{
PizzaMaker pizzamaker = new PizzaMaker();
BasePizza newPizza = pizzamaker.GetPizzaObject(PizzaMaker.PizzaNames.HamAndCheese);
if(newPizza is HamAndCheesePiza hamAndCheesePiza)
{
hamAndCheesePiza.Ham = 99;
hamAndCheesePiza.Cheese = 80;
}
// in the same way you treat other cases
}
Personally, I'd take a different tack altogether. I'd make a pizza a container to hold it's ingredients. I'd also take advantage of the collection initialization pattern (make a class enumerable, give it an appropriate void Add method, and you can initialize it like a Dictionary)
Something like this:
First some enums:
public enum Ingredient
{
Ham,
Pepperoni,
Pineapple,
etc,
}
public enum SauceType
{
Red,
White,
}
public enum PizzaSize
{
Personal,
Small,
Medium,
Large,
ExtraLarge,
}
then the Pizza class:
public class Pizza : IEnumerable<(Ingredient ingredient, int amountInGrams)>
{
private Dictionary<Ingredient, int> _ingredients = new Dictionary<Ingredient, int>();
private SauceType _sauceType;
private PizzaSize _size;
public Pizza(SauceType sauceType, PizzaSize size)
{
_sauceType = sauceType;
_size = size;
}
public void Add(Ingredient ingredient, int amountInGrams)
{
_ingredients.Add(ingredient, amountInGrams);
}
public IEnumerator<(Ingredient ingredient, int amountInGrams)> GetEnumerator()
{
foreach (var ingredient in _ingredients)
{
yield return (ingredient.Key, ingredient.Value);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public static void TestPizza()
{
var newPizza = new Pizza(SauceType.Red, PizzaSize.Medium)
{
{Ingredient.Ham, 200},
{Ingredient.Pepperoni, 250}
};
foreach (var ingredient in newPizza)
{
Debug.WriteLine($"Ingredient: {ingredient.ingredient} - {ingredient.amountInGrams} grams");
}
}
}
If I run the TestPizza method, I end up with this in the debug output:
Ingredient: Ham - 200 grams
Ingredient: Pepperoni - 250 grams
You might also want to have a mechanism to have a recipe (if it's a medium pizza, and it has Ham, then it's 200 grams, 350 grams if it's "Double Meat" (but, I'll leave that up to you)).
As to your "Pizza Maker" factory, just build a class that has a set of recipes (roughly a Dictionary<string, (SauceType sauce, Dictionary<Ingredient, int>)>) and when someone asks for a "The Works" pizza, you look up the recipe, build a pizza the way I showed above (except by explicitly calling Add) and returns the result.

Is there a way to derive or extend properties in C#?

I have a property where I want to convert the values systematically and I have a very large set of properties, so rather than have the following:
class myClass
{
private Double _Length;
public Double Length { get { return convert(_Length); } set { _Length = convertBack(value); }}
private Double _Height;
public Double Height{ get { return convert(_Height); } set { _Height= convertBack(value); }}
private Double _Width;
public Double Width{ get { return convert(_Width); } set { _Width= convertBack(value); }}
...
Double convert(Double base_value) { do work to return converted_value; }
Double unconvert(Double converted_value) { to work to return base_value; }
}
I would like to do something like this to reduce code pollution and redundancy
class myBaseClass
{
class DoublePropertyConverter extends Property
{
public Double get { return convert(this); }
public Double set { this = unconvert(value); }
}
Double convert(Double base_value) { do work to return converted_value; }
Double unconvert(Double converted_value) { to work to return base_value; }
}
class myClass : public myBaseClass
{
[DoublePropertyConverter]
public Double Length { get; set;}
[DoublePropertyConverter]
public Double Height{ get; set;}
[DoublePropertyConverter]
public Double Width{ get; set;}
...
}
Is this, or something like it, at all possible?
There's no way to "extend a property" in the way you're describing, no.
But it's easy enough to create a new type which represents conversions from and two some other value. Types like DateTime and TimeSpan are all just wrappers around a long that handle conversions to different semantic values for you, for example. Honestly it sounds like you should have a new type, because you've got a value that a consumer wants to treat in one way, but that is actually represented in memory as something else, and types are great at accomplishing just that in many situations that goes beyond the scope of getting and setting property values.
public class Foo
{
public Foo(double value)
{
underlyingValue = FromDouble(value);
}
private readonly object underlyingValue;
public double Value => ToDouble(underlyingValue);
public static implicit operator double(Foo foo) => ToDouble(foo.underlyingValue);
public static implicit operator Foo(double value) => new Foo(value);
private static double ToDouble(object underlyingVvalue)
{
throw new NotImplementedException();
}
private static object FromDouble(double value)
{
throw new NotImplementedException();
}
}
The underlying field in the type can be whatever you want that you're converting to/from, and you can then define your conversion logic in just one place.

How to correctly hide helpers (inner) classes inside a class in C#

I am having problems understanding how to correctly encapsulate my class. It is (or should be) an inmutable class.
I am using a "helper class" and I want it to be not accesible from the outside.
namespace MyApp
{
[Flags]
public enum OctaveGroups
{
None = 0,
oneOctave = 1
}
class Frequencies
{
// HelperClass
public class Frequency
{
public string Name { get; set; }
public OctaveGroups Octave { get; set; }
}
public readonly static List<Frequency> All = new List<Frequency>
{
#region Complete Frequencies Data
new Frequency { Name = "63", Hz = 63,
Octave = OctaveGroups.oneOctave | OctaveGroups.None,
},
new Frequency { Name = "80", Hz = 80,
Octave = OctaveGroups.None,
}
// And so on..
//..
#endregion
};
public readonly List<Frequency> OneOctave = All.Where(f => f.Octave.HasFlag(OctaveGroups.oneOctave)).ToList();
public readonly List<Frequency> None = All.Where(f => f.Octave.HasFlag(OctaveGroups.None)).ToList();
}
}
If I make my Frequency class protected or private I get this error:
Inconsistent accessibility: field type 'List'
is less accesible than field 'Frequencies.All'
I get the same error if I make class Frequency and List<Frequency> Allprotected and try to make a method that returns a List<Frequency> like:
public List<Frequency> GetAll()
{
return All.Where(f => f.Octave.HasFlag(OctaveGroups.OneOctave)).ToList();
}
How will be the correct way to expose just .All .OneOctave and .None fields while keeping them read only?
You can't expect to hide Frequency when you are planning in having public methods returning List<Frequency>.
Now, what I understand is your issue is that you need accessible property setters in Frequency from Frequencies but you don't want to expose them to the outside. The way to do this is through an interface that only exposes getters:
public interface IFrequency
{
string Name { get; }
OctaveGroups Octave { get; }
}
And now, you make Frequencies.Frequency a private nested class and you expose only IFrequency:
class Frequencies
{
// HelperClass
private class Frequency: IFrequency
{
public string Name { get; set; }
public OctaveGroups Octave { get; set; }
}
public readonly static List<IFrequency> All = new List<IFrequency>
{
#region Complete Frequencies Data
new Frequency { Name = "63", Hz = 63,
Octave = OctaveGroups.oneOctave | OctaveGroups.None,
},
new Frequency { Name = "80", Hz = 80,
Octave = OctaveGroups.None,
}
// And so on..
//..
#endregion
};
public readonly List<IFrequency> OneOctave = All.Where(f => f.Octave.HasFlag(OctaveGroups.oneOctave)).ToList();
public readonly List<IFrequency> None = All.Where(f => f.Octave.HasFlag(OctaveGroups.None)).ToList();
}
Now a consumer of Frequencies will only see IFrequency instances where no setter is exposed and is therefore immutable to the outside world (excluding reflection of course).
The correct way is not to hide them.
You simply cannot both hide and expose a class to the outside world, at the same time.
So if you want to declare a public method returning the object, or a collection of the object, you must make the type of the object in question public as well.
Try adding an internal constructor to your Frequency class. This will allow you to construct a Frequency from within the class but disallow outside classes from constructing it. Since it can't be constructed on the outside no one outside of the class will be able to add a new one to your Lists since they are typed to Frequency.
Example:
public class ExternalType
{
public class InternalType
{
internal InternalType(string someString)
{
SomeStringProp = someString;
}
public string SomeStringProp { get; private set; }
}
public readonly List<InternalType> InternalTypes = new List<InternalType>()
{
new InternalType("test")
};
}
If you try to instantiate InternalType outside of ExternalType you will get a compiler error. However your dependents will be able to read the list.

How to Create Two level enum

Sorry if the question's title is confusing,but i don't know how to ask it.
what is really want is to have read-only data that will never change.
currently i have two enums MeterType and SubMeterType
public enum MeterType
{
Water = 1001,
Electricity = 1004,
Gas = 1007
}
and
public enum SubMeterType
{
DrinkingWater = 1002,
UsageWater = 1003,
SubsidiseGas = 1008,
NonSusbsidisedGas = 1009
}
Now i would like to use these as follows
To get the MeterType
string meterType = MeterType.Water.ToString("d");
and to get the SubMeterType, is it possible to have something like
string subMeterType = MeterType.Water.DrinkingWater("d");
Shall go for another approach using classes with readonly properties ?
or modify these enums to suit my requirement.
Instead of using enums you might use constant integers in nested classes:
public static class MeterType
{
public const int Water = 1001;
public const int Electricity = 1004;
public const int Gas = 1007;
public static class Waters
{
public const int DrinkingWater = 1002;
public const int UsageWater = 1003;
}
public static class Gases
{
public const int SubsidiseGas = 1008;
public const int NonSusbsidisedGas = 1009;
}
}
Just use a nested enum:
public class MeterType
{
public enum Water { }
}
But in this case you can't use MeterType.Water directly, this is not possible by default. Try use nested objects then or a secondary enum for the MeterType.
public enum MeterType { }
public enum MeterTypeWater { }
In this case you need a property with a different name for each of the enums. Best solution is to not use a nested class:
public class MeterType
{
public static WaterType Water { get; }
}
public class WaterType
{
public readonly SubWaterType DrinkingWater = SubWaterType.DrinkingWater;
}
You cannot nest enums but you already know that. What you can do is to have const or readonly properties/fields which map to the various types you want. Then in each of the types, you define fields/properties for the subtypes.
public static class MeterTypes
{
public static readonly Electricity electricity;
public static readonly Gas gas;
public static readonly Water water;
static MeterTypes()
{
// initialize the meter types to their default
MeterTypes.Water = Water.GenericWater;
MeterTypes.Gas = Gas.GenericGas;
MeterTypes.Electricity = Electricity.GenericElectricity;
}
private MeterTypes()
{
// private initialization prevents others from creating the class
}
public class Electricity
{
public enum Type
{
Generic = 1007,
SubsidisedElectricity = 1008,
NonSubsidisedElectricity = 1009
}
public static readonly Electricity GenericElectricity;
public static readonly Electricity SubsidisedElectricity;
public static readonly Electricity NonSubsidisedElectricity;
private Type ElectricityType;
static Electricity()
{
SubsidisedElectricity = new Electricity(Type.SubsidisedElectricity);
NonSubsidisedElectricity = new Electricity(Type.NonSubsidisedElectricity);
GenericElectricity = new Electricity(Type.Generic);
}
// private constructor prevents creation from outside the class
private Electricity(Type ElectricityType)
{
this.ElectricityType = ElectricityType;
}
public override string ToString()
{
return ElectricityType.ToString();
}
public string ToString(string format)
{
return ElectricityType.ToString(format);
}
}
public class Gas
{
public enum Type
{
Generic = 1007,
SubsidisedGas = 1008,
NonSubsidisedGas = 1009
}
public static readonly Gas GenericGas;
public static readonly Gas SubsidisedGas;
public static readonly Gas NonSubsidisedGas;
private Type gasType;
static Gas()
{
SubsidisedGas = new Gas(Type.SubsidisedGas);
NonSubsidisedGas = new Gas(Type.NonSubsidisedGas);
GenericGas = new Gas(Type.Generic);
}
// private constructor prevents creation from outside the class
private Gas(Type gasType)
{
this.gasType = gasType;
}
public override string ToString()
{
return gasType.ToString();
}
public string ToString(string format)
{
return gasType.ToString(format);
}
}
public class Water
{
public enum Type
{
Generic = 1001,
DrinkingWater = 1002,
UsageWater = 1003
}
public static readonly Water GenericWater;
public static readonly Water DrinkingWater;
public static readonly Water UsageWater;
private Type waterType;
static Water()
{
DrinkingWater = new Water(Type.DrinkingWater);
UsageWater = new Water(Type.UsageWater);
GenericWater = new Water(Type.Generic);
}
// private constructor prevents creation from outside the class
private Water(Type waterType)
{
this.waterType = waterType;
}
public override string ToString()
{
return waterType.ToString();
}
public string ToString(string format)
{
return waterType.ToString(format);
}
}
}
This can be used as such
var w = MeterTypes.water; // will give generic water
var uw = MeterTypes.Water.UsageWater // will give usage water
and you get the added use of the Enum.ToString() methods too.
You'll have to note that this implementation relies on C#'s case sensitivity. This makes MeterTypes.electricity and MeterTypes.Electricity refer to a field and a class respectively. This code will is very likely to fail if it ever gets used in a language that is not case sensitive (e.g. VB.NET). You could circumvent this by using a different name for the static fields in the MeterTypes class (e.g. _Electricity instead of electricity).

Design a mutable class that after it's consumed becomes immutable

Suppose that the scenario doesn't allow to implement an immutable type. Following that assumption, I'd like opinions / examples on how to properly design a type that after it's consumed, becomes immutable.
public class ObjectAConfig {
private int _valueB;
private string _valueA;
internal bool Consumed { get; set; }
public int ValueB {
get { return _valueB; }
set
{
if (Consumed) throw new InvalidOperationException();
_valueB = value;
}
}
public string ValueA {
get { return _valueA; }
set
{
if (Consumed) throw new InvalidOperationException();
_valueA = value;
}
}
}
When ObjectA consumes ObjectAConfig:
public ObjectA {
public ObjectA(ObjectAConfig config) {
_config = config;
_config.Consumed = true;
}
}
I'm not satisfied that this simply works, I'd like to know if there's a better pattern (excluded, as said, making ObjectAConfig immutable by design from begin).
For example:
can make sense define a monad like Once<T> that allow the wrapped value to be initialized only once?
can make sense define a type that returns the type itself changing a private field?
What you are implementing sometimes goes under the name "popsicle immutability" - i.e. you can freeze it. Your current approach will work - indeed I use that pattern myself in numerous places.
You can probably reduce some duplication via something like:
private void SetField<T>(ref T field, T value) {
if (Consumed) throw new InvalidOperationException();
field = value;
}
public int ValueB {
get { return _valueB; }
set { SetField(ref _valueB, value); }
}
public string ValueA {
get { return _valueA; }
set { SetField(ref _valueA, value); }
}
There is another related approach, though: a builder. For example, taking your existing class:
public interface IConfig
{
string ValueA { get; }
int ValueB { get; }
}
public class ObjectAConfig : IConfig
{
private class ImmutableConfig : IConfig {
private readonly string valueA;
private readonly int valueB;
public ImmutableConfig(string valueA, int valueB)
{
this.valueA = valueA;
this.valueB = valueB;
}
}
public IConfig Build()
{
return new ImmutableConfig(ValueA, ValueB);
}
... snip: implementation of ObjectAConfig
}
Here there is a truly immutable implementation of IConfig, and your original implementation. If you want the frozen version, call Build().

Categories

Resources