I know how to do:
Class class = new Class();
class.Accessor
I don't know how to do:
class.Property.Accessor
class.Property.Subproperty.Accessor
class.Property.Subproperty.YetAnotherSubproperty.Accessor
Example analogy:
ListBox.Items.Count
An analogy to help explain (don't take it literally), I know how to create ListBox, I know how to create Count, but I don't know how to create Items
Something like
class Animal
{
// gets the heart of this animal
public HeartType Heart
{
get
{
...
}
}
}
class HeartType
{
// gets the number of beats per minute
public int Rate
{
get
{
...
}
}
}
Then for a given by var a = new Animal(); you could say
int exampleRate = a.Heart.Rate;
Well, your question is quite cryptic, but i'll try.
Subproperties are just instances of other classes (or static classes) with their own properties.
Example:
class Class1
{
public Class2Instance{get;set;}
public Class1()
{
Class2Instance =new Class2();
}
}
class Class2
{
public string Greeting = "Hello";
}
//////
var c = new Class1();
Console.WriteLine(c.Class2Instance.Greeting);
I'm not sure if I understood your question properly. But in your analogy, Items is a property/field of ListBox and the type of that property/field could be some collection. And that collection has the Count property.
I recently answered for similar question but it is more to do with a method chaining called FluentInterface style.
In you case, it seems you are trying to call properties/public fields, which are part of type; and these types are referenced in a nested fashion. With this in mind, the idea can be demonstrated as below;
class Program
{
static void Main(string[] args)
{
var one=new LevelOne();
Console.WriteLine(one.LevelTwo.LevelThree.LastLevel);
}
}
internal class LevelOne
{
public LevelOne()
{
LevelTwo = LevelTwo ?? new LevelTwo();
}
public LevelTwo LevelTwo { get; set; }
}
internal class LevelTwo
{
public LevelTwo()
{
LevelThree = LevelThree ?? new LevelThree();
}
public LevelThree LevelThree { get; set; }
}
internal class LevelThree
{
private string _lastLevel="SimpleString";
public String LastLevel { get { return _lastLevel; } set { _lastLevel = value; } }
}
Related
I need some help with an example of Inheritance class (abstract). I can't use my function ToCSV() because i return a list of BananaPart and C# wants a list of FruitPart. How is it possible to solve this ?
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
FruitFactory fruitFactory = new FruitFactory();
Fruit myFruit = fruitFactory.Create("banana");
myFruit.FruitPart.ForEach(f => Console.WriteLine(f.ToString()));
}
}
public class FruitFactory {
public Fruit Create(string fruitType) {
switch(fruitType) {
case "banana":
return new Banana();
break;
default:
throw new Exception("undefined fruitType");
}
}
}
public abstract class Fruit {
protected string _name;
protected List<FruitPart> _fruitPart;
public Fruit() {
this._name = "A fruit";
}
public string Name { get { return this._name; } }
public List<FruitPart> FruitPart { get { return this._fruitPart; } }
}
public abstract class FruitPart { }
public class Banana : Fruit {
public Banana() : base() {
this._name = "banana";
this._fruitPart = ToCSV();
}
public List<BananaPart> ToCSV(){
return new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
}
}
public class BananaPart : FruitPart {
private int _seed;
private string _dimensionPart;
public BananaPart (
int seed,
string dimensionPart
) {
this._seed = seed;
this._dimensionPart = dimensionPart;
}
}
It will be a pleasure for me to learn more about it ! Thank you in advance !
The reason that your code is not compiling is because List<T> is not covariant. In order to compile your code you need to change List<T> with IEnumerable<T> which has a covariant type parameter. So the changes are:
public abstract class Fruit {
protected string _name;
protected IEnumerable<FruitPart> _fruitPart;
public Fruit() {
this._name = "A fruit";
}
public string Name { get { return this._name; } }
public IEnumerable<FruitPart> FruitPart { get { return this._fruitPart; } }
}
You can learn more about covariance here: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/
In short, covariance lets you deduce the assignment compatibility of two types based on the assignment compatibility of two other types. Since, we can assign a BananaPart to a FruitPart, we can deduce that a IEnumerable<BananaPart> can be assigned to IEnumerable<FruitPart>.
There is a problem in your model. Say I have a Banana, and it has a list of fruit-parts. Now I could call myBananana.FruitParts.Add(new ApplePart()), this cannot work since a banana is only composed of bananaparts.
To avoid this you need to be more restrictive in what you return. Instead of using a List<FruitPart> you could use IEnumerable<Fruitpart>. This avoids the problem since you cannot add anything to a IEnumerable.
If you are learning I would also recommend starting with interfaces instead of abstract classes. The later is sometimes called "Implementation inheritance", i.e. it is used to share code between two derived classes. This is sometimes useful, but in the majority of cases it is better to put this shared code in a third class, sometimes called "Composition over inheritance".
Your fruit could ook something like this using interfaces:
public interface IFruit
{
string Name { get; }
IEnumerable<IFruitPart> FruitPart { get; }
}
public class Banana : IFruit
{
public Banana() : base() => FruitPart = ToCSV();
public static List<BananaPart> ToCSV() => new List<BananaPart> { new BananaPart(5, "10"), new BananaPart(10, "20"), new BananaPart(20, "40") };
public string Name { get; } = "Banana";
public IEnumerable<IFruitPart> FruitPart { get; }
}
public interface IFruitPart { }
public class BananaPart : IFruitPart
{
public int Seed { get; }
public string DimensionPart { get; }
public BananaPart(int seed, string dimensionPart) => (Seed, DimensionPart) = (seed, dimensionPart);
}
Notice that you do not need much more code for the interface variant compared to the abstract class case.
You cannot safely cast List<Child> to List<Parent> because it would let you add Apple to List<Banana>.
You can create List<FuitPart> instead.
public List<FruitPart> ToCSV()
{
return new List<FruitPart> {
new BananaPart(5, "10"),
new BananaPart(10, "20"),
new BananaPart(20, "40") };
}
As List<BananaPart> doesn't inherite List<FruitPart> you can't do it like that.
I'll suggest you to make some wrapper classes, like FruitedDetails and BananaDetails that would inherite it. That way you could encapsulate the specific details of classes you are working with and wouldn't have to deal with various casting of objects when you pack your lists.
My brain is gonna to explode. :) So I would like to get help from you.
Please, think about my question like about just programmer puzzle. (Actually. perhaps it is very easy question for you, but not for me.)
It is needed to create array of objects. For example List where T is class. (I will describe Class T below). Also it is needed create “container” that will contain this array and some methods for work with this array. For example Add(), Remove(int IndexToRemove).
Class T must have field "Container", this way each elements of our array would be able to know where is it contained and has access its container's fields and methods. Notice, that in this case Class T should have type parameter. Indeed, it is not known beforehand which container's type is used.
Let us denote this class container as A and class element (class T) as AUnit.
Code:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Add();
a.Units[0].SomeField +=100;
Console.ReadKey();
}
}
class A
{
public List<AUnit> Units;
public A()//ctor
{
Units = new List<AUnit>();
}
public void Add()
{
this.Units.Add(new AUnit(this));
}
}
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
}
Public fields should be protected or private of course, but let think about this later.
You can ask “why we create public A Container field in AUnit”? We create field public string Name{get;private set;} (actually property but nevermind). And also we would like to be able to change value of this field for example method [Class AUnit] public bool Rename(string newName)();. The main idea of this method is changing Name field only that case if no one element in array (public List Units; ) has the same name like newName. But to achieve this, Rename method has to have access to all names that is currently used. And that is why we need Container field.
Code of extended version AUnit
class AUnit
{
public int SomeField;
public A Container;
public string Name { get; private set; }
public AUnit(A container)
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (AUnit unt in this.Container.Units)
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
Ok. If you still read it let's continue. Now we need to create Class B and class BUnit which will be very similar like Class A and Class Aunit. And finally the main question of this puzzle is HOW WE CAN DO IT? Of course, I can CopyPaste and bit modify A and AUnit and create this code.
class B
{
public List<BUnit> Units; //Only Type Changing
public B()//ctor Name changing...
{
Units = new List<BUnit>();//Only Type Changing
}
public void Add()
{
this.Units.Add(new BUnit(this));//Only Type Changing
}
}
class BUnit
{
public int SomeField;
public B Container;//Only Type Changing
public string Name { get; private set; }
public A a; //NEW FIELD IS ADDED (just one)
public BUnit(B container) //Ctor Name and arguments type changing
{
this.SomeField = 43;
this.Container = container;
this.Name = "Default";
this.a=new A(); //New ROW (just one)
}
public bool Rename(String newName)
{
Boolean res = true;
foreach (BUnit unt in this.Container.Units) //Only Type Changing
{
if (unt.Name == newName)
{
res = false;
break;
}
}
if (res) this.Name = String.Copy(newName);
return res;
}
}
And I can to use this classes this way.
static void Main(string[] args)
{
B b = new B();
b.Add();
b.Units[0].a.Add();
b.Units[0].a.Units[0].SomeField += 100;
bool res= b.Units[0].a.Units[0].Rename("1");
res = b.Units[0].a.Units[0].Rename("1");
Console.ReadKey();
}
This construction is can be used to create “non-homogeneous trees”.
Help, I need somebody help, just no anybody…. [The Beatles]
I created B and BUnit using CopyPaste.
But how it can be done using “macro-definitions” or “Generic”, inherit or anything else in elegant style? (C# language)
I think that there is no reason to describe all my unsuccessful attempts and subquestions. Already topic is too long. : )
Thanks a lot if you still read it and understand what I would like to ask.
You need to implement a base type, lets call it UnitBase, with all common functionality. I'd structure your code the following way:
Create an interface for your container, this way you can change implementation to more performant solutions without modifying the elements you will be adding to the container.
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
}
Following the idea stated in 1, why not make the search logic belong to the container? It makes much more sense, as it will mostly depend on how the container is implemented:
public interface IContainer
{
Q Add<Q>() where Q : UnitBase, new();
IEnumerable<UnitBase> Units { get; }
bool Contains(string name);
}
A specific implementation of IContainer could be the following:
public class Container : IContainer
{
public Container()
{
list = new List<UnitBase>();
}
private List<UnitBase> list;
public Q Add<Q>() where Q: UnitBase, new()
{
var newItem = Activator.CreateInstance<Q>();
newItem.SetContainer(this);
list.Add(newItem);
return newItem;
}
public IEnumerable<UnitBase> Units => list.Select(i => i);
public bool Contains(string name) =>
Units.Any(unit => unit.Name == name);
}
Create a base class for your AUnit and BUnit types condensing all common functionality:
public abstract class UnitBase
{
protected UnitBase()
{
}
public IContainer Container { get; private set; }
public int SomeField;
public string Name { get; private set; }
public void SetContainer(IContainer container)
{
Container = container;
}
public bool Rename(String newName)
{
if (Container.Contains(newName))
return false;
this.Name = newName; //No need to use String.Copy
return true;
}
}
Implement your concrete types:
public class BUnit : UnitBase
{
public int SpecificBProperty { get; private set; }
public BUnit()
{
}
}
Shortcomings of this approach? Well, the container must be of type <UnitBase>, I've removed the generic type because it really wasn't doing much in this particular case as it would be invariant in the generic type.
Also, keep in mind that nothing in the type system avoids the following:
myContainer.Add<BUnit>();
myContainer.Add<AUnit>();
If having two different types in the same container is not an option then this whole set up kind of crumbles down. This issue was present in the previous solution too so its not something new, I simply forgot to point it out.
InBetween , I am very thankful to you for your advices. Actually I can't say that I understood your answer in full, but using your ideas I have done what I want.
Looks like my variant works well. However I would like to hear your (and everyone) opinions about code described below. The main goal of this structure is creating non-homogeneous trees. So could you estimate it from this side.
First of all. We need to create interfaces for both classes. We describe there all "cross-used" functions.
public interface IUnit<T>
{
string Name { get;}
void SetContainer(T t);
bool Rename(String newName);
}
public interface IContainer
{
bool IsNameBusy(String newName);
int Count { get; }
}
Next. Create Base for Unit Classes for future inheritance. We will use in this inheritors methods from Container Base so we need generic properties and IUnit interface.
class UnitBase<T> : IUnit<T> where T : IContainer
Unfortunately I don't know yet how to solve the problem with Constructor parameters. That is why I use method
SetContainer(T container).
Code:UnitBase
class UnitBase<T> : IUnit<T> where T : IContainer
{
protected T Container;
public string Name { get; private set; }
public UnitBase()
{
this.Name = "Default";
}
public void SetContainer(T container)
{
this.Container = container;
}
public bool Rename(String newName)
{
bool res = Container.IsNameBusy(newName);
if (!res) this.Name = String.Copy(newName);
return !res;
}
}
Next. Create ContainerBase
ContainerBase should:
1) has IContainer interface.
2)has information about what it will contain:
... where U : IUnit<C>, new()
3)and .... has information about what itself is. This information we need to pass as parameter to SetContainer() method.
Code ContainerBase:
class ContainerBase<U, C> : IContainer //U - Unit Class. C-Container Class
where U : IUnit<C>, new()
where C : ContainerBase<U, C>
{
protected List<U> Units;
public U this[int index] { get { return Units[index]; } }
public ContainerBase()//ctor
{
this.Units = new List<U>();
}
public void Add()
{
this.Units.Add(new U());
this.Units.Last().SetContainer(((C)this));//may be a bit strange but actualy this will have the same type as <C>
}
public bool IsNameBusy(String newName)
{
bool res = false;
foreach (var unt in this.Units)
{
if (unt.Name == newName)
{
res = true;
break;
}
}
return res;
}
public int Count { get { return this.Units.Count; } }
}
Cast ((TContainer)(this)) may be is a bit strange. But using ContainerBase we always should use NewInheritorContainer. So this cast is just do nothing…looks like...
Finally. This classes can be used like in this example.
class SheetContainer : ContainerBase<SheetUnit,SheetContainer> {public SheetContainer(){}}
class SheetUnit : UnitBase<SheetContainer>
{
public CellContainer Cells;
public PictureContainer Pictures;
public SheetUnit()
{
this.Cells = new CellContainer();
this.Pictures = new PictureContainer();
}
}
class CellContainer : ContainerBase<CellUnit, CellContainer> { public CellContainer() { } }
class CellUnit : UnitBase<CellContainer>
{
public string ValuePr;//Private Field
private const string ValuePrDefault = "Default";
public string Value//Property for Value
{
//All below are Just For Example.
get
{
return this.ValuePr;
}
set
{
if (String.IsNullOrEmpty(value))
{
this.ValuePr = ValuePrDefault;
}
else
{
this.ValuePr = String.Copy(value);
}
}
}
public CellUnit()
{
this.ValuePr = ValuePrDefault;
}
}
class PictureContainer : ContainerBase<PictureUnit, PictureContainer> { public PictureContainer() { } }
class PictureUnit : UnitBase<PictureContainer>
{
public int[,] Pixels{get;private set;}
public PictureUnit()
{
this.Pixels=new int[,]{{10,20,30},{11,12,13}};
}
public int GetSizeX()
{
return this.Pixels.GetLength(1);
}
public int GetSizeY()
{
return this.Pixels.GetLength(0);
}
public bool LoadFromFile(string path)
{
return false;
}
}
static void Main(string[] args)
{
SheetContainer Sheets = new SheetContainer();
Sheets.Add();
Sheets.Add();
Sheets.Add();
Sheets[0].Pictures.Add();
Sheets[1].Cells.Add();
Sheets[2].Pictures.Add();
Sheets[2].Cells.Add();
Sheets[2].Cells[0].Value = "FirstTest";
bool res= Sheets[0].Rename("First");//res=true
res=Sheets[2].Rename("First");//res =false
int res2 = Sheets.Count;
res2 = Sheets[2].Pictures[0].Pixels[1, 2];//13
res2 = Sheets[2].Pictures.Count;//1
res2 = Sheets[1].Pictures.Count;//0
res2 = Sheets[0].Pictures[0].GetSizeX();//3
Console.ReadKey();
}
Looks like it works like I want. But I didn’t test it full.
Let me say Thank you again, InBetween.
interface IFly<T>
{
T GetMark();
}
public class Bird : IFly<string>
{
public string GetMark()
{
return "Bird";
}
}
public class Plane : IFly<int>
{
public int GetMark()
{
return 123;
}
}
class Program
{
static void Main()
{
IFly<string> bird = new Bird();
IFly<int> plane = new Plane();
Console.WriteLine(bird.GetMark());
Console.WriteLine(plane.GetMark());
Console.ReadKey();
}
}
I would like to replace this
IFly<string> bird = new Bird();
IFly<int> plane = new Plane();
with something like this:
var fly = new List<IFly<T>>
Any suggestions?
This might work:
public interface IFlyRoot { }
interface IFly<T> : IFlyRoot
{
T GetMark();
}
Then you can make a List<IFlyRoot>.
In general case #Steve's answer is the right one. However, depending on your needs you could try using type variance, but it doesn't support value types (int wouldn't work).
Notice the out in interface definition:
interface IFly<out T>
{
T GetMark();
}
Then you are allowed to write:
var list = new List<IFly<object>>();
list.Add(bird);
but it won't work with Plane, where T is a value type (here: int). This solution may not suit your exact needs then.
To get more insight into why variance doesn't work with value types refer to answers of Jon Skeet or Eric Lippert. In short, it's because reference identity should be preserved, but with value types it cannot. A value type would always be boxed first, loosing that identity. That's why it doesn't work automatically. There's not really a clean way around this. One thing you could try is to make Plane class explicitly implement IFly<object> as well:
public class Plane : IFly<int>, IFly<object>
{
public int GetMark()
{
return 123;
}
object IFly<object>.GetMark()
{
return GetMark();
}
}
And add to list:
list.Add(new Plane());
Since IFly and IFly are considered different types by the CLR you will need to establish a more direct relationship between the two, in this case by implementing a non-generic version of the interface IFly. The collection would then store objects as long as they implement this common interface for example:
class Program
{
static void Main()
{
var flyingThings = new ThingsThatFlyCollection();
var bird = new Bird();
var bird2 = new Bird();
var plane = new Plane();
flyingThings.Add(bird);
flyingThings.Add(bird2);
flyingThings.Add(plane);
Console.WriteLine(flyingThings.GetItemWithCast<string>(0).GetMark());
Console.WriteLine(flyingThings.GetItemWithCast<string>(1).GetMark());
Console.WriteLine(flyingThings.GetItemWithCast<int>(2).GetMark());
foreach (var item in flyingThings.GetItemsWithCast<int>())
{
Console.WriteLine(item.GetMark());
}
foreach (var item in flyingThings.GetItemsWithCast<string>())
{
Console.WriteLine(item.GetMark());
}
foreach (var item in flyingThings.GetItemsByType<Bird>())
{
Console.WriteLine(item.GetMark());
}
Console.ReadKey();
}
}
public interface IFly
{
object GetMark();
}
public interface IFly<TMark> : IFly
{
new TMark GetMark();
}
class Plane : IFly<int>
{
public int GetMark() { return 123; }
object IFly.GetMark() { return this.GetMark(); }
}
class Bird : IFly<string>
{
public string GetMark() { return "Bird"; }
object IFly.GetMark() { return this.GetMark(); }
}
class ThingsThatFlyCollection : Collection<IFly>
{
public IFly<TMark> GetItemWithCast<TMark>(int index)
{
var f = this[index] as IFly<TMark>;
if (f == null) { throw new InvalidCastException(); }
return f;
}
public IEnumerable<IFly<TMark>> GetItemsWithCast<TMark>()
{
var items = this.Where(p => p is IFly<TMark>).Cast<IFly<TMark>>();
return items;
}
public IEnumerable<TFlyer> GetItemsByType<TFlyer>() where TFlyer : IFly
{
var items = this.Where(p => p.GetType() == typeof(TFlyer)).Cast<TFlyer>();
return items;
}
}
Keep in mind that upcasting can have some performance implications depending on transaction volume, however is probably negligible for most scenarios.
I need to refactor the following class:
public interface IEmployee
{
int VacationWeeks { get; }
int YearsWithCompany { set; get; }
double Salary { set; get; }
}
public class Employee : IEmployee
{
private readonly int vacationWeeks;
public Employee(int vacationWeeks)
{
this.vacationWeeks = vacationWeeks;
}
public int VacationWeeks
{
get { return vacationWeeks; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
I need to make sure that VacationWeeks depends only on YearsWithCompany, and I am loading the mapping from the database. So far I have come up with this:
public class EmployeeNew : IEmployee
{
private Dictionary<int,int> vacationWeeksTable;
public EmployeeNew(Dictionary<int, int> vacationWeeksTable)
{
this.vacationWeeksTable = vacationWeeksTable;
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
public int YearsWithCompany { set; get; }
public double Salary { set; get; }
}
This class implements what I want, but it still has one vulnerability: different instances of EmployeeNew in the same collection may have been created with different instances of vacationWeeksTable.
All instances of EmployeeNew in the same collection must refer to the same vacationWeeksTable.
The application I am refactoring uses lots of List all over the system, and we need to be able to modify YearsWithCompany and Salary, yet to guarantee that only one vacationWeeksTable is used per List. These lists are iterated several times; its elements are modified in each iteration.
Here is my imperfect solution. Suggestions are welcome:
// this class does two things, which I do not like
public class EmployeeList : IEnumerable<IEmployee>, IEmployee
{
private Dictionary<int, int> vacationWeeksTable;
private List<EmployeeSpecificData> employees;
private int currentIndex;
private EmployeeSpecificData CurrentEmployee
{
get { return employees[currentIndex]; }
}
public IEnumerator<IEmployee> GetEnumerator()
{
for (currentIndex = 0; currentIndex < employees.Count; currentIndex++)
{
yield return this;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int VacationWeeks
{
get { return vacationWeeksTable[YearsWithCompany]; }
}
// this is ugly repetitive code I don't like
public int YearsWithCompany
{
get { return CurrentEmployee.YearsWithCompany; }
set { CurrentEmployee.YearsWithCompany = value; }
}
// this is ugly repetitive code I don't like
public double Salary
{
get { return CurrentEmployee.Salary; }
set { CurrentEmployee.Salary = value; }
}
}
I use the following to create and init some of the classes that need default and shared behaviour. Maybe if you can refactor it will help:
It is some form of the Factory and FlyWeight patterns combined (the flyweight part can be removed in your scenario), which in addition has a concept of class Type shared handlers.
I simplified and removed some stuff that you wont need but there is more to remove, I added comments.
Usage would be: (app init)
Dictionary<int,int> vacationWeeksTable = new Dictionary<int,int>();
// fill the table
Factory<Employee>.Init(vacationWeeksTable);
The whenever you create a Employee class:
// remove grouping in the factory class to remove this null
Employee em = Factory<Employee>.Create(null);
It takes only a WeakReference to the classes so you don't have to worry about GC.
Each employee will have the shared vacationWeeksTable setup on creation, without the possibility to change it after from outside if not using the factory class.
You could change the vacation table for all running instances of Employee at any moment in the runtime of the app with:
// this will call the method registered for SetInitialdata on all instances of Employee classes.
// again remove grouping to remove that null
Factory<Employee>.Call(EventHandlerTypes.SetInitialData, null, vacTable);
Sample implementation of Employee:
class Employee : IBaseClass
{
private Dictionary<int, int> vacationWeeksTable;
public virtual void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> register)
{
group = 0; // disable different groups
register(new Key<Employee, int>(0), group, EventHandlerTypes.SetInitialData, SetVacationWeeksTable);
}
public virtual void RegisterSharedData(Action<IKey, object> regData)
{
// remove this from factory and interface, you probably dont need it
// I have been using it as a FlyWeight data store for classes.
}
private void SetVacationWeeksTable(object sender, SharedEventArgs e)
{
vacationWeeksTable = e.GetData<Dictionary<int, int>>();
}
}
Code pattern Implementation:
IBaseClass : interface that each of my classes that are creatable through a factory implement
public enum EventHandlerTypes
{
SetInitialData // you can add additional shared handlers here and Factory<C>.Call - it.
}
public class SharedEventArgs : EventArgs
{
private object data;
public SharedEventArgs(object data)
{
this.data = data;
}
public T GetData<T>()
{
return (T)data;
}
}
public interface IBaseClass
{
void RegisterSharedHandlers(int? group, Action<IKey, int?, EventHandlerTypes, Action<object, SharedEventArgs>> regEvent);
void RegisterSharedData(Action<IKey, object> regData);
}
Utility generic classes:
public interface IKey
{
Type GetKeyType();
V GetValue<V>();
}
public class Key<T, V> : IKey
{
public V ID { get; set; }
public Key(V id)
{
ID = id;
}
public Type GetKeyType()
{
return typeof(T);
}
public Tp GetValue<Tp>()
{
return (Tp)(object)ID;
}
}
public class Triple<T, V, Z>
{
public T First { get; set; }
public V Second { get; set; }
public Z Third { get; set; }
public Triple(T first, V second, Z third)
{
First = first;
Second = second;
Third = third;
}
}
Factory class with slight modification to handle your scenario:
public static class Factory<C> where C : IBaseClass, new()
{
private static object initialData;
private static Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>> handlers = new Dictionary<IKey, Triple<EventHandlerTypes, int, WeakReference>>();
private static Dictionary<IKey, object> data = new Dictionary<IKey, object>();
static Factory()
{
C newClass = new C();
newClass.RegisterSharedData(registerSharedData);
}
public static void Init<IT>(IT initData)
{
initialData = initData;
}
public static Dt[] GetData<Dt>()
{
var dataList = from d in data where d.Key.GetKeyType() == typeof(Dt) select d.Value;
return dataList.Cast<Dt>().ToArray();
}
private static void registerSharedData(IKey key, object value)
{
data.Add(key, value);
}
public static C Create(int? group)
{
C newClass = new C();
newClass.RegisterSharedHandlers(group, registerSharedHandlers);
// this is a bit bad here since it will call it on all instances
// it would be better if you can call this from outside after creating all the classes
Factory<C>.Call(EventHandlerTypes.SetInitialData, null, initialData);
return newClass;
}
private static void registerSharedHandlers(IKey subscriber, int? group, EventHandlerTypes type, Action<object, SharedEventArgs> handler)
{
handlers.Add(subscriber, new Triple<EventHandlerTypes, int, WeakReference>(type, group ?? -1, new WeakReference(handler)));
}
public static void Call<N>(EventHandlerTypes type, int? group, N data)
{
Call<N>(null, type, group, data);
}
public static void Call<N>(object sender, EventHandlerTypes type, int? group, N data)
{
lock (handlers)
{
var invalid = from h in handlers where h.Value.Third.Target == null select h.Key;
// delete expired references
foreach (var inv in invalid.ToList()) handlers.Remove(inv);
var events = from h in handlers where h.Value.First == type && (!#group.HasValue || h.Value.Second == (int)#group) select h.Value.Third;
foreach (var ev in events.ToList())
{
// call the handler
((Action<object, SharedEventArgs>)ev.Target)(sender, arg);
}
}
}
}
Make a class which contains a Dictionary. Creating or getting instance of this new class will load the dictionary in a consistent way. Then your BOs can take an instance of the class, thus ensuring they're all using the same data (because the class containingthe list knows how to load itself with the proper set of data).
I need a base class with a property where I can derive classes with the same property but different (compatible) types. The base Class can be abstract.
public class Base
{
public virtual object prop { get; set; }
}
public class StrBase : Base
{
public override string prop { get; set; } // compiler error
}
public class UseIt
{
public void use()
{
List<Base> l = new List<Base>();
//...
}
}
I tried it with Generics but that gives me a problem when using the class, because I want to store differently typed base classes in the List.
public class BaseG<T>
{
public T prop { get; set; }
}
public class UseIt
{
public void use()
{
List<BaseG> l = new List<BaseG>(); // requires type argument
//...
}
}
Here's an alternative approach to proposed solution:
public abstract class Base
{
public abstract void Use();
public abstract object GetProp();
}
public abstract class GenericBase<T> : Base
{
public T Prop { get; set; }
public override object GetProp()
{
return Prop;
}
}
public class StrBase : GenericBase<string>
{
public override void Use()
{
Console.WriteLine("Using string: {0}", Prop);
}
}
public class IntBase : GenericBase<int>
{
public override void Use()
{
Console.WriteLine("Using int: {0}", Prop);
}
}
Basically I've added a generic class in the middle that stores your properly-typed property. this will work assuming that you never need to access Prop from the code that iterates the members of the List<Base>. (You could always add an abstract method to Base called GetProp that casts the generic to an object if that's required.)
Sample usage:
class Program
{
static void Main(string[] args)
{
List<Base> l = new List<Base>();
l.Add(new StrBase {Prop = "foo"});
l.Add(new IntBase {Prop = 42});
Console.WriteLine("Using each item");
foreach (var o in l)
{
o.Use();
}
Console.WriteLine("Done");
Console.ReadKey();
}
}
Edit: Added the GetProp() method to illustrate how the property can be directly accessed from the base class.
You can't override the type of a property. Take a look at the following code:
StrBase s = new StrBase();
Base b = s;
This is completely valid code. But what happens when you try to do this?
b.prop = 5;
The integer can be converted to object, because everything is derived from object. But since b is actually a StrBase instance, it would have to convert the integer to a string somehow, which it can't. So that is why you aren't allowed to override the type.
The same principle applies to generics:
List<BaseG<object>> l = new List<BaseG<object>>();
BaseG<string> s = new BaseG<string>();
// The compiler will not allow this.
l.add(s);
// Here's the same problem, convert integer to string?
BaseG<object> o = l[0];
o.prop = 5;
This is because generic types in C# 2.0 are invariant. C# 4.0 does allow this type of conversions, called covariance and contravariance.
Solutions
An option is to cast the object back to string when you need it. You could add type validation in the subclass:
public class StrBase : Base
{
private string propValue;
public override object prop {
get
{
return this.propValue;
}
set
{
if (value is string)
{
this.propValue = (string)value;
}
}
}
}
You could also expose a type-safe property in the subclass:
public class StrBase : Base
{
public string strProp {
get
{
return (string)this.prop;
}
set
{
this.prop = value;
}
}
}
This is possible since C# 9.0
Beginning with C# 9.0, override methods support covariant return types.
(see Microsoft docs)
public class First
{
private int someV;
public virtual object SomeV { get => someV; set => someV = (int)value; }
public First() { }
}
public class Two : First
{
private string someV;
public override object SomeV { get => someV; set => someV = value.ToString(); }
public Two() { }
}
and use of those:
First firstClass = new First();
firstClass.SomeV = 1;
Two twoClass = new Two();
twoClass.SomeV = "abcd";