Imagine a factory that produces objects of a certain type but keeps a WeakReference to them. This factory can tell us how many objects of a particular type are alive. But what if I want to replace every single one of those objects with an entirely different instance, but have every single location in code where this instance is used be automatically updated. How do I do it?
For example, given the following scenario:
public interface ITheme
{
string TextColor { get; }
string BgrColor { get; }
}
class LightTheme : ITheme
{
public string TextColor => "black";
public string BgrColor => "white";
}
class DarkTheme : ITheme
{
public string TextColor => "white";
public string BgrColor => "dark gray";
}
I can construct a tracking factory that simply tracks objects it creates:
public class TrackingThemeFactory
{
private readonly List<WeakReference<ITheme>> themes = new();
public ITheme CreateTheme(bool dark)
{
ITheme theme = dark ? new DarkTheme() : new LightTheme();
themes.Add(new WeakReference<ITheme>(theme));
return theme;
}
public string Info
{
get
{
var sb = new StringBuilder();
foreach (var reference in themes)
{
if (reference.TryGetTarget(out var theme))
{
bool dark = theme is DarkTheme;
sb.Append(dark ? "Dark" : "Light");
sb.AppendLine(" theme");
}
}
return sb.ToString();
}
}
}
If, however, I want to replace every constructed instance, I end up having to create a type with an extra level of indirection
public class Ref<T> where T : class
{
public T Value;
public Ref(T value)
{
Value = value;
}
}
To be used in
public class ReplaceableThemeFactory
{
private readonly List<WeakReference<Ref<ITheme>>> themes
= new();
private ITheme createThemeImpl(bool dark)
{
return dark ? new DarkTheme() : new LightTheme();
}
public Ref<ITheme> CreateTheme(bool dark)
{
var r = new Ref<ITheme>(createThemeImpl(dark));
themes.Add(new(r));
return r;
}
public void ReplaceTheme(bool dark)
{
foreach (var wr in themes)
{
if (wr.TryGetTarget(out var reference))
{
reference.Value = createThemeImpl(dark);
}
}
}
}
This does in fact work:
var factory2 = new ReplaceableThemeFactory();
var magicTheme = factory2.CreateTheme(true);
Console.WriteLine(magicTheme.Value.BgrColor); // dark gray
factory2.ReplaceTheme(false);
Console.WriteLine(magicTheme.Value.BgrColor); // white
But I'm wondering if there's a better way.
Related
i am currently working on a project, as the project builds up into lots of lines of code, so i am trying to take the trivial approach and start using some smarter way to make the code be reusable.
so for instance i need to connect to the Sql Server:
(it could be any other task i am learning while i code i am aware of all availble .Net built-in dedicated classes)
really all i need is :
SqlCommand, SqlDataReader (&SqlConnection) and the appropriate container object to return data
usually i am using List<dictionary<string, object>> for my tests with database
so till now it is very minimal i should think.
but, then i say ah that's a good way to connect to sql and fetch some rowsets
then i decide that this is not enough, cause there's some other kinds of returned data, so let's just make some alternative to cover more options, actually let's cover all available scenarios, and then i come up with a class full of "models" covering all available data Formats, single record multiple rows datasets, etc' .
but that's not all there's also 2 main options to read from sql server from .NET
like SqlDataReader And SqlDataAdapter. so i will cover those options too, but wait , there's also the magic words i could use such as stored procedurs and plain text command , so lets make a class that will hold some stractures to define a stored procedure, and then there's storedprocedure parameters, lets make an easy way to call a stored procedure as an object, so later one-line-call and we have a stored procedure object...
now if i will stop right here cause this is yet only the beginning there's much more in that rabbit hole.. and that even was not the best example as the objects created has a very good reason to exist, well unless you want to code it more efficiently you will not create those extra classes and objects
with this behavior and some more tasks to cover on a "daily basis" i begin to see there's a pattern that leads to a huge .Cs file that contains around 10 namespaces with Dozens of classes and functions on each.
the question is how do you decide when to use minimal code and when do you let it go and do not care a lot cause hey, the work station is on say .. 8 cores I7 machine there would not be much of a difference, so lets just worry about the ease of coding.
where do you put the red line ?
Edit the code below is not for a code review purpose, it's just to show the scale of the application and the performance impact it could lead to.**
this is only the core of the data object factory, thats about 1/3 of the class which one of a few in other realted tasks.
public class Inventory
{
public class DataCariers
{
public class DbDataCar
{
public Prototypes.DataCarPrototypes.CarTypeMeta CarierSpec { get; set; }
public AlsDataCariers.Inventory.Compartments.DataCarCompartment Trunk { get; set; }
public string CarierName { get; private set; }
public DbDataCar(AlsTransMods.Direction bsDir, AlsTransMods.CarSize bsCs, AlsTransMods.useCar bsFamType, Compartments.DataCarCompartment bsTrunk)
{
this.CarierSpec = new Prototypes.DataCarPrototypes.CarTypeMeta()
{
CarDirection = bsDir,
carOfSize = bsCs,
CarFamilySelected = bsFamType
};
this.Trunk = bsTrunk;
this.Trunk.Value = new object();
this.Trunk.compTypeMeta.CompartmentParent = this.GetType();
}
public DbDataCar(Prototypes.DataCarPrototypes.CarTypeMeta bsCarierSpec, Compartments.DataCarCompartment bsTrunk)
{
this.CarierSpec = bsCarierSpec;
this.Trunk = bsTrunk;
this.Trunk.Value = new object();
this.Trunk.compTypeMeta.CompartmentParent = this.GetType();
}
internal void SetTunk(string CsvVal)
{
//this.Trunk = new AlsDataCariers.Inventory.Compartments.GenericsDataCarCompartment.Singles(CsvVal);
if (this.CarierSpec.CarDirection == AlsTransMods.Direction.In)
{
// new Compartments.MultipleRowsTabedMultipleRecordsCompartmnet(new prototypesFactory.DataCarPrototypes.selectionsTypeCreator(prototypesFactory.DataCarPrototypes.SelectionMode.selectionMultyRow).SelectedSelectionType);
}
else this.Trunk.Value = new Csv() { val = CsvVal };
}
}
public class GenericDbDataCar : DbDataCar
{
public GenericDbDataCar(AlsTransMods.Direction bsDir, AlsTransMods.CarSize bsCs, AlsTransMods.useCar bsFamType, Compartments.DataCarCompartment bsTrunk)
: base(bsDir, bsCs, bsFamType, bsTrunk)
{
this.CarierSpec = new Prototypes.DataCarPrototypes.CarTypeMeta();
this.CarierSpec.CarDirection = bsDir;
this.CarierSpec.carOfSize = bsCs;
this.CarierSpec.CarFamilySelected = bsFamType;
this.CarierSpec.CarOfType = this.GetType();
base.CarierSpec = this.CarierSpec;
//this.Trunk = this.TrunkCreate();
//base.Trunk = this.Trunk;
}
public GenericDbDataCar(Prototypes.DataCarPrototypes.CarTypeMeta bsCarierSpec, Compartments.DataCarCompartment bsTrunk)
: base(bsCarierSpec, bsTrunk)
{
this.CarierSpec.CarOfType = this.GetType();
base.CarierSpec = this.CarierSpec;
}
}
public class TabularDbDataCar : DbDataCar
{
public TabularDbDataCar(AlsTransMods.Direction bsDir, AlsTransMods.CarSize bsCs, AlsTransMods.useCar bsFamType, Compartments.DataCarCompartment bsTrunk)
: base(bsDir, bsCs, bsFamType, bsTrunk)
{
this.CarierSpec = new Prototypes.DataCarPrototypes.CarTypeMeta();
//this.CarierName = string.Concat(bsCs, bsFamType);
this.CarierSpec.CarDirection = bsDir;
this.CarierSpec.carOfSize = bsCs;
this.CarierSpec.CarFamilySelected = bsFamType;
this.CarierSpec.CarOfType = this.GetType();
base.CarierSpec = this.CarierSpec;
}
public TabularDbDataCar(Prototypes.DataCarPrototypes.CarTypeMeta bsCarierSpec, Compartments.DataCarCompartment bsTrunk):base(bsCarierSpec, bsTrunk)
{
this.CarierSpec.CarOfType = this.GetType();
}
}
}
public class Compartments
{
public class DataCarCompartment
{
//private Prototypes.DataCarPrototypes.selectionsType selectionsType;
public RobCs509b.MyModels.Prototypes.DataCarPrototypes.CompartmentMeta compTypeMeta{get; private set;}
public DataCarCompartment(RobCs509b.MyModels.Prototypes.DataCarPrototypes.CompartmentMeta Bs_CompTypeMeta)
{
this.compTypeMeta = Bs_CompTypeMeta;
this.compTypeMeta.CompartmentSeed = this.GetType();
}
public virtual object Value { get; set; }
}
public class TabularDataCarCompartment : DataCarCompartment
{
public TabularDataCarCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentFamilyType = this.GetType();
}
}
public class TabedSingleRecordComartment : TabularDataCarCompartment
{
public TabedSingleRecordComartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
this.Value = new DataColumn();
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
}
public new DataColumn Value;
}
public class TabedMultipleRecordsCompartment : TabularDataCarCompartment
{
public TabedMultipleRecordsCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType= this.GetType();
}
}
public class TabedSingleRowMultipleRecordsCompartment : TabedMultipleRecordsCompartment
{
public TabedSingleRowMultipleRecordsCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
base.Value = this.Value;
}
public new DataRow Value;
}
public class MultipleRowsTabedMultipleRecordsCompartmnet : TabedMultipleRecordsCompartment
{
public MultipleRowsTabedMultipleRecordsCompartmnet(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
this.Value = new DataTable();
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
base.Value = this.Value;
}
public new DataTable Value;
}
public class MultipleRowsClonedTabedMultipleRecordsCompartmnet : TabedMultipleRecordsCompartment
{
public MultipleRowsClonedTabedMultipleRecordsCompartmnet(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
this.Value = new ClonedSchemaDtt("NotSet");
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
}
public new ClonedSchemaDtt Value;
}
public class GenericDataCarCompartment : DataCarCompartment
{
public GenericDataCarCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentFamilyType = this.GetType();
}
}
public class GenericSingelRecordCompartment : GenericDataCarCompartment
{
public GenericSingelRecordCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
}
}
public class GenericSingleCsv :GenericSingelRecordCompartment
{
public GenericSingleCsv(string CsvVal,Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
this.Value = new Csv();
this.Value.val = CsvVal;
}
public new Csv Value;
}
public class GenericMultipleRecordsCompartment : GenericDataCarCompartment
{
public GenericMultipleRecordsCompartment(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
}
//public RepetablevaluesCollections RepeatbleCollNameVal;
//public UniqCollectionNameval UniqCollNameVal;
}
public class GenericSingleRowMultipleRecordsCompartment3s: GenericMultipleRecordsCompartment
{
public GenericSingleRowMultipleRecordsCompartment3s(Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
this.Value = new MyGenericObject3<string, string, string>("", "", "");
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
}
/// <summary>
/// MyGenericObject3 (string,string,string) values.
/// </summary>
public new MyGenericObject3<string, string, string> Value;
}
public class GenericMultipleRowsMyGenericObj3DataCarCompartment:GenericMultipleRecordsCompartment
{
public GenericMultipleRowsMyGenericObj3DataCarCompartment (Prototypes.DataCarPrototypes.CompartmentMeta compartmentMeta)
: base(compartmentMeta)
{
base.compTypeMeta.CompartmentDeliverBaseType = this.GetType();
this.Value = new List<MyGenericObject3<string, string, string>>();
base.compTypeMeta.CompartmentDeliverType = this.Value.GetType();
base.Value = this.Value;
}
/// <summary>
/// DbMagaZine (List MyGenereicObject3) valueType
/// </summary>
public new List<MyGenericObject3<string, string, string>> Value;
}
}
public class CompartmentTypes
{
private CompartmentTypes creator;
public CompartmentTypes ()
{
}
}
}
public class Compartments
{
public class Tabular :Inventory.CompartmentTypes
{
Type CompartmentOfFamilyType = typeof(System.ComponentModel.MarshalByValueComponent);
Type CompartmnetOfType = typeof(Tabular);
}
public class Generic : Inventory.CompartmentTypes
{
Type CompartmnetOfType = typeof(Generic);
}
}
The problem of yours is, that there are so many options to do the same thing.
My advise is to use the technology that is easiest to program and only switch to lower level abstractions if absolutely necessary for perfomance reasons.
And don't compare performance for simple cases but think about the whole system performane and maintenance.
I did database programming in the pre Entity Framework times, and it takes much more expertise and effort to this correctly, so I would not recommend this as of today.
So I reccommend technology in the following order:
Entity Framework pure
Entity Framework with stored procedures for special cases
Datasets and hand crafted logic
Datasets with stored procedures for special cases
SqlDatareader for read only sequential access of a whole table
I might have coded myself into a corner here, but I am hoping there is a simple way out of it.
My Logic Layer is a static singleton instance that has lots of properties for accessing the individual sub-Logic Layers. Most of this was put in place for unit testing to allow injection of custom repositories and works quite nicely for that. However the boiler plate code for each sub-instance is very repetitive and I would expect that there is a way to simplify it.
Below is a very simplified example to demonstrate how far I have manages to get and where I am stuck. The one way that works is with an indexer on the sub-logic class wrapper to get to the sub-logic, which reads very strangely. The other way is by using a random letter as a property on the sub-logic class wrapper to get to the sub-logic. This reads slightly better, but still has a ripple effect on all the existing code.
Can this be done the way I want to, or should I be looking at this completely differently.
Apologies for the length of the example. I tried to make it as simple as possible while still keeping the concept intact
class Program
{
static void Main(string[] args)
{
var logic = new Logic();
// old usage (desired)
var abcs_1 = logic.ABCs_1.List();
var defs_1 = logic.DEFs_1.List();
// new usage (would like to keep old way)
var abcs_2 = logic.ABCs_2[0].List(); // <-- ugly
var defs_2 = logic.DEFs_2.d.List(); // <-- less ugly, but still not pretty
//var abcs_2 = logic.ABCs_2.List(); // <-- wanted
//var defs_2 = logic.DEFs_2.List(); // <-- wanted
}
}
public class ABC { }
public class DEF { }
public class ABCsLogicLayer
{
public List<ABC> List() { return null; }
}
public class DEFsLogicLayer
{
public List<DEF> List() { return null; }
}
public class Logic
{
#region New Code. Want to move towards this
public LogicLocker<ABCsLogicLayer> ABCs_2 = new LogicLocker<ABCsLogicLayer>();
public LogicLocker<DEFsLogicLayer> DEFs_2 = new LogicLocker<DEFsLogicLayer>();
#endregion
#region Old Code. Want to move away from this.
#region BuilerPlate for ABCs_1
private ABCsLogicLayer m_ABCs = null;
private readonly object m_ABCsLock = new object();
public ABCsLogicLayer ABCs_1
{
get
{
lock (m_ABCsLock)
{
if (m_ABCs == null)
{
m_ABCs = new ABCsLogicLayer();
}
}
return m_ABCs;
}
set
{
lock (m_ABCsLock)
{
m_ABCs = value;
}
}
}
#endregion
#region BuilerPlate for DEFs_1
private DEFsLogicLayer m_DEFs = null;
private readonly object m_DEFsLock = new object();
public DEFsLogicLayer DEFs_1
{
get
{
lock (m_DEFsLock)
{
if (m_DEFs == null)
{
m_DEFs = new DEFsLogicLayer();
}
}
return m_DEFs;
}
set
{
lock (m_DEFsLock)
{
m_DEFs = value;
}
}
}
#endregion
#endregion
}
public class LogicLocker<T> where T : class, new()
{
private T LogicLayer = null;
private readonly object LogicLayerLock = new object();
public T this[int i]
{
get
{
lock (LogicLayerLock)
{
if (LogicLayer == null)
{
LogicLayer = new T();
}
}
return LogicLayer;
}
set
{
lock (LogicLayerLock)
{
LogicLayer = value;
}
}
}
public T d
{
get
{
lock (LogicLayerLock)
{
if (LogicLayer == null)
{
LogicLayer = new T();
}
}
return LogicLayer;
}
set
{
lock (LogicLayerLock)
{
LogicLayer = value;
}
}
}
}
You can use interface for solve your problem:
1) Define interface with List method:
public interface ILogicLayer<T>
{
List<T> List();
}
2) Your logic layer class should implement this interface:
public class ABCsLogicLayer:ILogicLayer<ABC>
3) LogicLocker should implement this interface as well
public class LogicLocker<T,U> : ILogicLayer<U> where T : ILogicLayer<U>, new()
Implementation of List method will be:
lock (LogicLayerLock)
{
if (LogicLayer == null)
{
LogicLayer = new T();
}
}
return LogicLayer.List();
4) You will instantiate your logic locker in the Logic class
public LogicLocker<ABCsLogicLayer, ABC> ABCs_2 = new LogicLocker<ABCsLogicLayer,ABC>();
public LogicLocker<DEFsLogicLayer, DEF> DEFs_2 = new LogicLocker<DEFsLogicLayer, DEF>();
or better make it more generics:
public LogicLocker<LogicLayer<ABC>> ABCs_2 = new LogicLocker<LogicLayer<ABC>>();
but it depends on what you want
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);
}
public class BlockType
{
public static BlockType Dirt_Block = new Block("Blah! values here");
}
public struct Block
{
public static string Value;
public Block(string value)
{
value = value;
}
}
Is there any way I can get the value from DirtBlock? BlockType.Dirt_Block.Value Dosent work, Im not sure if this is possible, if not any ways to get the same result? (Theres more values, but I shortened it for size) Btw, Im accessing BlockType.Dirt.value from anouther class. I only want to get one value out of it though
That's because Dirt_Block is a BlockType, not a Block. In fact, I wouldn't expect this code to compile, as there is no way of converting a Block to a BlockType.
I'm not quite sure what you're after, but it sounds like you have a collection of block types that would suit an enumeration:
public enum BlockType
{
Dirt,
Wood,
Metal
}
and you have a collection of Blocks that each have a block type:
public class Block
{
public BlockType Type { get; set; }
}
Here, we are using a public auto property. You should avoid using public fields (no getters or setters).
Now, to create a new Block, you can use object initialisation:
var block = new Block { Type = BlockType.Metal };
Alternatively, you could create a Block constructor which takes a BlockType parameter.
Other way to achieve it.
I think this approah provides an easier way to load/save tilemaps, and let defining block properties from text if needed.
Strict OOP with virtual methods and intensive inheritance is not a good idea for a game that may need optimizations to run fine.
Of course it can be improved or done in other way. ;)
public struct BlockProperty
{
public Texture2D Texture;
public string Name;
}
public class BlockProperties
{
static readonly List<BlockProperty> Blocks;
public static readonly BlockProperty Dirt;
public static readonly BlockProperty Grass;
public static readonly BlockProperty Wall;
static BlockProperties( )
{
ContentManager Content = Game1.Instance.Content;
Blocks = new List<BlockProperty>( )
{
(Dirt = new BlockProperty( ) { Name = "Dirt", Texture = Content.Load<Texture2D>( "textures/dirt" ) }),
(Grass = new BlockProperty( ) { Name = "Grass", Texture = Content.Load<Texture2D>( "textures/grass" ) }),
(Wall = new BlockProperty( ) { Name = "Wall", Texture = Content.Load<Texture2D>( "textures/wall" ) }),
};
}
public static BlockProperty ByName( string name )
{
return Blocks.FirstOrDefault( b => b.Name == name );
}
}
public class Block
{
public BlockProperty BlockType;
pulic Vector2 Position;
public Block( BlockProperty block )
{
this.BlockType = block;
}
public Block( string block )
{
this.BlockType = BlockProperties.ByName(block);
}
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
public static Game1 Instance { get; private set; }
Block Block;
public Game1( )
{
Instance = this;
graphics = new GraphicsDeviceManager( this );
}
protected override void LoadContent( )
{
Block = new Block("Dirt") { Position = new Vector2(100,100) };
}
}
Assuming you mean Dirt_Block to be of type Block - your Block Value field is static - that means it is associated with the Block type, not any particular instance. Make it a public field (or property) and your access will work:
public class BlockType
{
public static Block Dirt_Block = new Block("Blah! values here");
}
public struct Block
{
public string Value;
public Block(string value)
{
value = value;
}
}
Also if there is no particular reason you are going with a struct here, I would suggest making Block a class and expose Value as a public property.
Your doubt isn't very clear... but I think what you want is something like this... using OOP paradigm on the block classes.
public class BlockType
{
public virtual string Value;
public static BlockType Dirt = new Block("Blah! values here");
}
public class Block : BlockType
{
private string value;
public Block(string value)
{
this.value = value;
}
public string Value
{
get { return value; }
}
}
Right?
I've been studying up on static reflection with LINQ expressions - very cool!
One thought I had - is it possible for one class to 'generate' fields on one class based on static reflection done on another class? I'm thinking specifically of the Builder pattern I've seen here many times. I would like to do a fluent-nhibernate-style property registration that 'generates' fields on the builder that match the class I want to build. Soemthing like this:
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public class Builder : BuilderBase<Color>
{
public Builder()
{
Property(x => x.Name);
}
public Build()
{
return built_up_color;
}
}
}
and support constructor syntax like this:
Color c = new Color.Builder() { Name = "Red" }.Build();
The point of all this is to reduce the number of times I have to repeat defining the properies of Color. I played with this:
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public class Builder
{
private Color _color = new Color();
public string Name
{
get { return _color.Name; }
set { _color.Name = value; }
}
public Build()
{
return _color;
}
}
}
which certainly works AND lists the properties the same # of times but feels wordier and less flexible. It seems like I should be able to do something anonymous-type-y here?
Worth pointing out that Having a class called Color clashing with the System.Drawing.Color is probably a bad idea.
It is very likely to lead to confusion in others (worse still System.Drawring.Color has value semantics whereas your class has reference semantics which can lead to further confusion)
I would point out that what you really want is Named Optional Arguments. I would suggest that putting in cumbersome Builder classes now will be more effort and made it more painful to move to these once you get to c# 4.0. Instead make the constructors that are required (or if need be to avoid type signature collisions static factory methods on the class)
I think it's impossible, you can't generate members except by declaring them explicitly. Bite the bullet and declare a constructor for Color.
PS: I think static reflection is a misnomer, the only thing that's static is the lookup of the member you want to reference — a good thing as far as it goes, but that's not very far.
less code to write but using reflection to set the values.
The trick is to use collection initializers. it is typesafe.
public class Color
{
private Color()
{
}
public string Name { get; private set; }
public int Prop2 { get; private set; }
public class Builder : Builder<Color>
{
public Builder()
{
// possible
_instance.Name = "SomeDefaultValue";
}
}
}
class Builder<T> : IEnumerable<string>
{
protected T _instance = Activator.CreateInstance(typeof(T));
public void Add<TProperty>(Expression<Func<T, TProperty>> prop, TProperty value)
{
StaticReflection.GetPropertyInfo(prop).SetValue(_instance, value, null);
}
public static implicit operator T(Builder<T> builder)
{
return builder.Build();
}
public T Build()
{
return _instance;
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
// e.g. return iterator over the property names
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable<string>)this).GetEnumerator();
}
}
and call syntax
var color = new Color.Builder
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
}.Build();
// or
var color = new Builder<Color>
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
}.Build();
// or
Color color = new Builder<Color>
{
{ x => x.Name, "foo" },
{ x => x.Prop2, 5 }
};