In my project I have a generic Packet class. I would like to be able to upcast to other classes (like LoginPacket or MovePacket).
The base class contains a command and arguments (greatly simplified):
public class Packet
{
public String Command;
public String[] Arguments;
}
I would like to have be able to convert from Packet to LoginPacket (or any other) based on a check if Packet.Command == "LOGIN". The login packet would not contain any new data members, but only methods for accessing specific arguments. For example:
public class LoginPacket : Packet
{
public String Username
{
get { return Arguments[0]; }
set { Arguments[0] == value; }
}
public String Password
{
get { return Arguments[1]; }
set { Arguments[1] == value; }
}
}
It would be great if I could run a simple code that would cast from Packet to LoginPacket with something like LoginPacket _Login = (LoginPacket)_Packet;, but that throws a System.InvalidCastException.
It seems like this would be an easy task, as no new data is included, but I can't figure out any other way than copying everything from the Packet class to a new LoginPacket class.
A better approach is to make Packet instance encapsulated by LoginPacket.
This will allow you to do:
LoginPacket _Login = new LoginPacket(_packet);
Also consider creating a PacketFactory where all the logic needed to create various Packet's goes in.
public class Packet
{
public String Command;
public String[] Arguments;
}
public abstract class AbstractPacket
{
private Packet _packet;
public AbstractPacket(Packet packet)
{
_packet = packet;
}
public string this[int index]
{
get { return _packet.Arguments[index]; }
set { _packet.Arguments[index] = value; }
}
}
public class LoginPacket : AbstractPacket
{
public LoginPacket(Packet packet): base(packet)
{
}
public string Username
{
get { return base[0]; }
set { base[0] = value; }
}
public string Password
{
get { return base[1]; }
set { base[1] = value; }
}
}
If different type of Packets differ only by available data members then you could do something below:
Use PacketGenerator to generate packets as:
Packet packet = PacketGenerator.GetInstance(packetdata);
Access the properties as:
Console.WriteLine("User Name: {0}", packet["UserName"]);
Code..
public enum PacketType { Undefined, LoginPacket, MovePacket }
public class PacketData
{
public String Command;
public String[] Arguments;
}
public class Packet
{
public readonly PacketType TypeOfPacket;
private Dictionary<string, string> _argumentMap;
public Packet(PacketType _packetType,
Dictionary<string, string> argumentMap)
{
TypeOfPacket = _packetType;
_argumentMap = argumentMap;
}
public string this[string index]
{
get { return _argumentMap[index]; }
set { _argumentMap[index] = value; }
}
}
public static class PacketFactory
{
Packet GetInstance(PacketData packetData)
{
Dictionary<string, string> argumentMap
= new Dictionary<string, string>();
PacketType typeOfPacket = PacketType.Undefined;
// Replace inline strings/int with static/int string definitions
switch (packetData.Command.ToUpper())
{
case "LOGIN":
typeOfPacket = PacketType.LoginPacket;
argumentMap["UserName"] = packetData.Arguments[0];
argumentMap["PassWord"] = packetData.Arguments[1];
break;
case "MOVE":
typeOfPacket = PacketType.MovePacket;
//..
break;
default:
throw new ArgumentException("Not a valid packet type");
}
return new Packet(typeOfPacket, argumentMap);
}
}
Related
I have created an interface that in theory should be able to return multiple generic lists of different types to provide the client with various information. When I attempt to loop through the results of the list it is only able to return first collection, can you help me to understand how I should be returning results from the following:
Interface class:
public interface IExampleInterface{}
public class ExampleType : IExampleInterface
{
public int First;
public int Last;
}
public class ExampleAmount : IExampleInterface
{
public decimal Amount;
public decimal TotalFee;
}
public class ExampleFacts : IExampleInterface
{
public bool TooLow;
public bool TooHigh;
}
Interface provider:
public class ExampleInterfaceProvider
{
private static readonly string conn = ConfigurationManager.ConnectionStrings["conn"].ConnectionString;
public static List<IExampleInterface> ExampleResults(int id)
{
//declare variables, read from database query using ExecuteReader...
var sT = new ExampleType
{
First = first;
Last = last;
}
var sA = new ExampleAmount
{
Amount = amount;
TotalFee = totalFee;
}
var sF = new ExampleFacts
{
TooHigh = tooHigh;
TooLow = tooLow;
}
var exampleResults = new List<IExampleInterface> {sT, sA, sF};
return exampleResults;
}
}
On the page I need to return the data:
foreach (dynamic item in ExampleResults(0))
{
Response.Write(item.First.ToString())
Response.Write(item.Last.ToString())
//The first two for 'sT' read fine, it breaks here
Response.Write(item.Amount.ToString())
//... And so on
}
Any help would be much appreciated,
Thanks
I think, there is no another solution except comparing implementations;
foreach (IExampleInterface item in ExampleResults(0))
{
if (item is ExampleType)
{
var exampleType = (ExampleType)item;
Response.Write(exampleType.First.ToString())
Response.Write(exampleType.Last.ToString())
}
else if (item is ExampleAmount)
{
var exampleAmount = (ExampleAmount)item;
Response.Write(exampleAmount.Amount.ToString())
}
//... And so on
}
If you are using C# 7, you can perform it as switch case
foreach (IExampleInterface item in ExampleResults(0))
{
switch (item)
{
case ExampleType c:
Response.Write(c.First.ToString());
Response.Write(c.Last.ToString());
break;
case ExampleAmount c:
Response.Write(c.Amount.ToString());
break;
default:
break;
}
//... And so on
}
You can find the documentation.
So basically, the items implementing IExampleInterface should all be written to a Response in a way that is somewhat specific to the actual type implementing the interface?
Then how about this:
public interface IExampleInterface
{
void WriteTo(Response response);
}
public class ExampleType : IExampleInterface
{
public int First;
public int Last;
public void WriteTo(Response response)
{
response.Write(First.ToString());
response.Write(Last.ToString());
}
}
public class ExampleAmount : IExampleInterface
{
public decimal Amount;
public decimal TotalFee;
public void WriteTo(Response response)
{
response.Write(Amount.ToString());
response.Write(TotalFee.ToString());
}
}
public class ExampleFacts : IExampleInterface
{
public bool TooLow;
public bool TooHigh;
public void WriteTo(Response response)
{
response.Write(TooLow.ToString());
response.Write(TooHigh.ToString());
}
}
And then:
foreach (IExampleInterface item in ExampleResults(0))
{
item.WriteTo(Response);
}
Assuming that Response is a variable holding an instance of the response rather than a static class.
Looking for design guidelines for the following problem.
I'm receiving two string values - action and message and have to call appropriate method which processes string message (processM1MessageVer1, processM1MessageVer2, processM2MessageVer1...). The method I have to call depends on the given string action. There are 2 versions (but in future there might be more) of each processing method. The version of method I have to call is determined by global variable version. Every method returns object of different type (ResultObject1, ResultObject2...). The result has to be serialized, converted to base64 and returned back.
Is there more elegant way of writing this (eliminate duplicate code, make possible future changes easier, reduce code...):
string usingVersion = "ver1";
public string processRequest(string action, string message)
if (usingVersion == "ver1"){
processRequestVer1(action, message);
}
else{
processRequestVer2(action, message);
}
}
//version 1
public string processRequestVer1(string action, string message){
string result = "";
switch (action){
case "m1":
ResultObject1 ro = processM1MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
case "m2":
ResultObject2 ro = processM2MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
case "m3":
ResultObject3 ro = processM3MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
}
return result;
}
//version 2
public string processRequestVer2(string action, string message){
string result = "";
switch (action){
case "m1":
ResultObject1 ro = processM1MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
case "m2":
ResultObject2 ro = processM2MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
case "m3":
ResultObject3 ro = processM3MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
}
return result;
}
It would be simplier if messages that have to be processed are of different object types instead of strings so that appropriate method could be called polymorphically. The fact that every process method returns different object type also complicates things even more. But these don't depend on me and I cannot change it.
My approach (make it more object oriented, and you should justify whether it's appropriate to create class structure depending on how complex your processing logic is. If your processing logic is only little then maybe this is over-engineering):
For serialize and convert to base 64, I assume you have some logic to do those tasks in a generic way. If not, move those to sub class also
public interface IRequestProcessorFactory
{
IRequestProcessor GetProcessor(string action);
}
public class FactoryVersion1 : IRequestProcessorFactory
{
public IRequestProcessor GetProcessor(string action)
{
switch(action)
{
case "m1":
return new M1Ver1RequestProcessor();
case "m2":
return new M2Ver1RequestProcessor();
case "m3":
return new M3Ver1RequestProcessor();
default:
throw new NotSupportedException();
}
}
}
public class FactoryVersion2 : IRequestProcessorFactory
{
public IRequestProcessor GetProcessor(string action)
{
switch(action)
{
case "m1":
return new M1Ver2RequestProcessor();
case "m2":
return new M2Ver2RequestProcessor();
case "m3":
return new M3Ver2RequestProcessor();
default:
throw new NotSupportedException();
}
}
}
public interface IRequestProcessor
{
string ProcessRequest(string message);
}
public class RequestProcessorBase<T>
{
public string ProcessRequest(string message)
{
T result = Process(message);
string serializedResult = Serialize(result);
return ConvertToB64(serializedResult);
}
protected abstract T Process(string message);
private string Serialize(T result)
{
//Serialize
}
private string ConvertToB64(string serializedResult)
{
//Convert
}
}
public class M1Ver1RequestProcessor : RequestProcessorBase<ResultObject1>
{
protected ResultObject1 Process(string message)
{
//processing
}
}
public class M2Ver1RequestProcessor : RequestProcessorBase<ResultObject2>
{
protected ResultObject2 Process(string message)
{
//processing
}
}
public class M3Ver1RequestProcessor : RequestProcessorBase<ResultObject3>
{
protected ResultObject3 Process(string message)
{
//processing
}
}
public class M1Ver2RequestProcessor : RequestProcessorBase<ResultObject1>
{
protected ResultObject1 Process(string message)
{
//processing
}
}
public class M2Ver2RequestProcessor : RequestProcessorBase<ResultObject2>
{
protected ResultObject2 Process(string message)
{
//processing
}
}
public class M3Ver2RequestProcessor : RequestProcessorBase<ResultObject3>
{
protected ResultObject3 Process(string message)
{
//processing
}
}
Usage:
string action = "...";
string message = "...";
IRequestProcessorFactory factory = new FactoryVersion1();
IRequestProcessor processor = factory.GetProcessor(action);
string result = processor.ProcessRequest(message);
The switch is still there in factory class, but it only returns processor and doesn't do actual work so it's fine for me
First - define interface that suit you best, like this
public interface IProcessMessage
{
string ActionVersion { get; }
string AlgorithmVersion { get; }
string ProcessMessage(string message);
}
Then create as many implementation as you need
public class processorM1Ver1 : IProcessMessage
{
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM1MessageVer1(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
public string ActionVersion {get { return "m1"; }}
public string AlgorithmVersion {get { return "ver1"; }}
}
public class processorM2Ver1 : IProcessMessage
{
public string ActionVersion {get { return "m2"; }}
public string AlgorithmVersion {get { return "ver1"; }}
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM2MessageVer1(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
}
public class processorM1Ver2 : IProcessMessage
{
public string ActionVersion {get { return "m1"; }}
public string AlgorithmVersion {get { return "ver2"; }}
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM1MessageVer2(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
}
Now you need something that know which implementation is best in current context
public class MessageProcessorFactory
{
private MessageProcessorFactory() { }
private static readonly MessageProcessorFactory _instance = new MessageProcessorFactory();
public static MessageProcessorFactory Instance { get { return _instance; }}
private IEnumerable<IProcessMessage> _processorCollection;
IEnumerable<IProcessMessage> ProcessorCollection
{
get
{
if (_processorCollection == null)
{
//use reflection to find all imlementation of IProcessMessage
//or initialize it manualy
_processorCollection = new List<IProcessMessage>()
{
new processorM1Ver1(),
new processorM2Ver1(),
new processorM1Ver2()
};
}
return _processorCollection;
}
}
internal IProcessMessage GetProcessor(string action)
{
var algorithVersion = ReadAlgorithVersion();
var processor = ProcessorCollection.FirstOrDefault(x => x.AlgorithmVersion == algorithVersion && x.ActionVersion == action);
return processor;
}
private string ReadAlgorithVersion()
{
//read from config file
//or from database
//or where this info it is kept
return "ver1";
}
}
It can be use in such way
public class Client
{
public string ProcessRequest(string action, string message)
{
IProcessMessage processor = MessageProcessorFactory.Instance.GetProcessor(action);
return processor.ProcessMessage(message);
}
}
The question is rather about serialized generic list<>
I have used a tool xsd2code for generating a serialized class file from xml schema to generate xml file on given data.
The class file contains all the xml data fields variables into classes as bellow-
public partial class Awmds
{
private List bol_segmentField;
public Awmds()
{
this.bol_segmentField = new List<AwmdsBol_segment>();
}
public List<AwmdsBol_segment> Bol_segment
{
get
{
return this.bol_segmentField;
}
set
{
this.bol_segmentField = value;
}
}
}
public partial class AwmdsBol_segment
{
private AwmdsBol_segmentBol_id bol_idField;
private sbyte consolidated_CargoField;
private AwmdsBol_segmentLoad_unload_place load_unload_placeField;
private AwmdsBol_segmentTraders_segment traders_segmentField;
private List<AwmdsBol_segmentCtn_segment> ctn_segmentField;
private AwmdsBol_segmentGoods_segment goods_segmentField;
private string value_segmentField;
public AwmdsBol_segment()
{
this.goods_segmentField = new AwmdsBol_segmentGoods_segment();
this.ctn_segmentField = new List<AwmdsBol_segmentCtn_segment>();
this.traders_segmentField = new AwmdsBol_segmentTraders_segment();
this.load_unload_placeField = new AwmdsBol_segmentLoad_unload_place();
this.bol_idField = new AwmdsBol_segmentBol_id();
}
public AwmdsBol_segmentBol_id Bol_id
{
get
{
return this.bol_idField;
}
set
{
this.bol_idField = value;
}
}
public sbyte Consolidated_Cargo
{
get
{
return this.consolidated_CargoField;
}
set
{
this.consolidated_CargoField = value;
}
}
.... and so on for other fields ....
}
public partial class AwmdsBol_segmentBol_id
{
private string bol_referenceField;
private sbyte line_numberField;
private sbyte bol_natureField;
private string bol_type_codeField;
public string Bol_reference
{
get
{
return this.bol_referenceField;
}
set
{
this.bol_referenceField = value;
}
}
public sbyte Line_number
{
get
{
return this.line_numberField;
}
set
{
this.line_numberField = value;
}
}
public sbyte Bol_nature
{
get
{
return this.bol_natureField;
}
set
{
this.bol_natureField = value;
}
}
public string Bol_type_code
{
get
{
return this.bol_type_codeField;
}
set
{
this.bol_type_codeField = value;
}
}
}
.... and so on for other classes ....
I have all the data to fill the generic list: List bol_segmentField
My problem is I dont know how to insert the data to the members of List bol_segmentField based on the class file.
Someone please help me to fillup the generic list by the class variables.
Maybe I am missing something, but would this work:
var awds = new Awmds();
var segment = new AwmdsBol_segment();
// here fill in the segment
awds.Bol_segment.Add(segment);
I have say 3 classes, Animal, Cat & Dog.
// calling code
var x = new Animal("Rex"); // would like this to return a dog type
var x = new Animal("Mittens"); // would like this to return a cat type
if(x.GetType() == typeof(Dog))
{
x.Bark();
}
else
{
x.Meow();
}
class Animal
{
public Animal(string name)
{
// check against some list of dog names ... find rex
// return Animal of type Dog.
// if not...
// check against some list of cat names ... find mittens
// return Animal of type Cat.
}
}
Is this possible somehow? If not is there something similar I can do?
What you are looking for is either a 'virtual constructor' (not possibe in C#) or the Factory pattern.
class Animal
{
// Factory method
public static Animal Create(string name)
{
Animal animal = null;
... // some logic based on 'name'
animal = new Zebra();
return animal;
}
}
The Factory method can also be placed in another (Factory) class. That gives better decoupling etc.
No. Basically the right fix is to use a static method which can create an instance of the right type:
var x = Animal.ForName("Rex");
var x = Animal.ForName("Mittens");
...
public abstract class Animal
{
public static Animal ForName(string name)
{
if (dogNames.Contains(name))
{
return new Dog(name);
}
else
{
return new Cat(name);
}
}
}
Or this could be an instance method in an AnimalFactory type (or whatever). That would be a more extensible approach - the factory could implement an interface, for example, and could be injected into the class which needed to create the instances. It really depends on the context though - sometimes that approach is overkill.
Basically, a new Foo(...) call always creates an instance of exactly Foo. Whereas a static method declared with a return type of Foo can return a reference to any type which is compatible with Foo.
No I dont think it is possible in the way that you want.
You could create a static class that has a method that returns an animal based on a name e.g.
static Animal CreateAnimal(string name)
{
if(catList.Contains(name))
return new Cat(name");
else if(dogList.Contains(name))
return new Dog(name);
return null;
}
The other answers show that you need to use a factory pattern but I wanted to give you a more "practical" example of how you would do it. I did exactly what you where doing, however I was working with the EPL2 printer language. When I saw X I needed to create a instance of class Rectangle, when I saw A I needed to create a instance of class Text.
(I wrote this a long time ago so I am sure some of the things I did could be improved upon).
public partial class Epl2CommandFactory
{
#region Singelton pattern
private static volatile Epl2CommandFactory m_instance;
private static object m_syncRoot = new object();
public static Epl2CommandFactory Instance
{
get
{
if (m_instance == null)
{
lock (m_syncRoot)
{
if (m_instance == null)
{
m_instance = new Epl2CommandFactory();
}
}
}
return m_instance;
}
}
#endregion
#region Constructor
private Epl2CommandFactory()
{
m_generalCommands = new Dictionary<string, Type>();
Initialize();
}
#endregion
#region Variables
private Dictionary<string, Type> m_generalCommands;
private Assembly m_asm;
#endregion
#region Helpers
private void Initialize()
{
Assembly asm = Assembly.GetAssembly(GetType());
Type[] allTypes = asm.GetTypes();
foreach (Type type in allTypes)
{
// Only scan classes that are not abstract
if (type.IsClass && !type.IsAbstract)
{
// If a class implements the IEpl2FactoryProduct interface,
// which allows retrieval of the product class key...
Type iEpl2FactoryProduct = type.GetInterface("IEpl2GeneralFactoryProduct");
if (iEpl2FactoryProduct != null)
{
// Create a temporary instance of that class...
object inst = asm.CreateInstance(type.FullName);
if (inst != null)
{
// And generate the product classes key
IEpl2GeneralFactoryProduct keyDesc = (IEpl2GeneralFactoryProduct)inst;
string key = keyDesc.GetFactoryKey();
m_generalCommands.Add(key, type);
inst = null;
}
}
}
}
m_asm = asm;
}
#endregion
#region Methods
public IEpl2Command CreateEpl2Command(string command)
{
if (command == null)
throw new NullReferenceException("Invalid command supplied, must be " +
"non-null.");
Type type;
if (!m_generalCommands.TryGetValue(command.Substring(0, 2), out type))
m_generalCommands.TryGetValue(command.Substring(0, 1), out type);
if (type != default(Type))
{
object inst = m_asm.CreateInstance(type.FullName, true,
BindingFlags.CreateInstance,
null, null, null, null);
if (inst == null)
throw new NullReferenceException("Null product instance. " +
"Unable to create necessary product class.");
IEpl2Command prod = (IEpl2Command)inst;
prod.CommandString = command;
return prod;
}
else
{
return null;
}
}
#endregion
}
The way the code works is I use the singleton pattern to create a factory class so people can call var command = Epl2CommandFactory.Instance.CreateEpl2Command("..."); passing in the EPL2 command string and it returns a instance of the class that represents that specific class.
During initialization I use reflection to find classes that support the IEpl2GeneralFactoryProduct interface, if the class supports the interface the factory stores the one or two letter code representing the printer command in a dictionary of types.
When you try to create the command the factory looks up the printer command in the dictionary and creates the correct class, it then passes the full command string on to that class for further processing.
Here is a copy of a command class and it's parents if you wanted to see it
Rectangle:
[XmlInclude(typeof(Rectangle))]
public abstract partial class Epl2CommandBase { }
/// <summary>
/// Use this command to draw a box shape.
/// </summary>
public class Rectangle : DrawableItemBase, IEpl2GeneralFactoryProduct
{
#region Constructors
public Rectangle() : base() { }
public Rectangle(Point startingLocation, int horozontalEndPosition, int verticalEndPosition)
: base(startingLocation)
{
HorizontalEndPosition = horozontalEndPosition;
VerticalEndPosition = verticalEndPosition;
}
public Rectangle(int x, int y, int lineThickness, int horozontalEndPosition, int verticalEndPosition)
: base(x, y)
{
LineThickness = lineThickness;
HorizontalEndPosition = horozontalEndPosition;
VerticalEndPosition = verticalEndPosition;
}
#endregion
#region Properties
[XmlIgnore]
public int LineThickness { get; set; }
[XmlIgnore]
public int HorizontalEndPosition {get; set;}
[XmlIgnore]
public int VerticalEndPosition { get; set; }
public override string CommandString
{
get
{
return String.Format("X{0},{1},{2},{3},{4}", X, Y, LineThickness, HorizontalEndPosition, VerticalEndPosition);
}
set
{
GenerateCommandFromText(value);
}
}
#endregion
#region Helpers
private void GenerateCommandFromText(string command)
{
if (!command.StartsWith(GetFactoryKey()))
throw new ArgumentException("Command must begin with " + GetFactoryKey());
string[] commands = command.Substring(1).Split(',');
this.X = int.Parse(commands[0]);
this.Y = int.Parse(commands[1]);
this.LineThickness = int.Parse(commands[2]);
this.HorizontalEndPosition = int.Parse(commands[3]);
this.VerticalEndPosition = int.Parse(commands[4]);
}
#endregion
#region Members
public override void Paint(Graphics g, Image buffer)
{
using (Pen p = new Pen(Color.Black, LineThickness))
{
g.DrawRectangle(p, new System.Drawing.Rectangle(X, Y, HorizontalEndPosition - X, VerticalEndPosition - Y));
}
}
public string GetFactoryKey()
{
return "X";
}
#endregion
}
DrawableItemBase:
public abstract class DrawableItemBase : Epl2CommandBase, IDrawableCommand
{
protected DrawableItemBase()
{
Location = new Point();
}
protected DrawableItemBase(Point location)
{
Location = location;
}
protected DrawableItemBase(int x, int y)
{
Location = new Point();
X = x;
Y = y;
}
private Point _Location;
[XmlIgnore]
public virtual Point Location
{
get { return _Location; }
set { _Location = value; }
}
[XmlIgnore]
public int X
{
get { return _Location.X; }
set { _Location.X = value; }
}
[XmlIgnore]
public int Y
{
get { return _Location.Y; }
set { _Location.Y = value; }
}
abstract public void Paint(Graphics g, Image buffer);
}
Epl2CommandBase:
public abstract partial class Epl2CommandBase : IEpl2Command
{
protected Epl2CommandBase() { }
public virtual byte[] GenerateByteCommand()
{
return Encoding.ASCII.GetBytes(CommandString + '\n');
}
public abstract string CommandString { get; set; }
}
Various Interfaces:
public interface IEpl2GeneralFactoryProduct
{
string GetFactoryKey();
}
public interface IEpl2Command
{
string CommandString { get; set; }
}
public interface IDrawableCommand : IEpl2Command
{
void Paint(System.Drawing.Graphics g, System.Drawing.Image buffer);
}
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().