I have 2 independent classes A and B and I have a Storage class which manages the storage of objects of type A and B.
I am trying to abstract the code that does the Store of A and B, however I am stuck due to List covariance I could not assign List<object> objList = new List<A>(); in the following code.
[DataContract]
public class A {
public int UID;
}
[DataContract]
public class B {
public int UID;
}
public class Storage {
public void Store(A a) {
List<A> aList = ReadA();
if (aList == null) {
aList = new List<A>();
}
aList.Add(a);
WriteNodes(aList);
}
public void StoreB(B b) {
List<B> bList = ReadB();
if (bList == null) {
bList = new List<B>();
}
bList.Add(b);
WriteNodes(bList);
}
public List<A> ReadA() {
//deserializes from aFileName and returns List<A>
}
public List<B> ReadB() {
//deserializes from bFileName adn returns List<B>
}
private static void WriteNodes<T>(List<T> nodeList) {
FileStream fs = new FileStream(aFileName, FileMode.Create);
XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(fs);
DataContractSerializer ser =
new DataContractSerializer(typeof(List<T>));
ser.WriteObject(writer, nodeList);
writer.Close();
fs.Close();
}
}
If you look at StoreA and StoreB methods they have a generic pattern except for the type that is used. ReadA and ReadB are no problem I could just take the type as another parameter and create a single function Read.
So is it possible to create an abstraction for Store so I don't end up with StoreA and StoreB methods?
What about:
public void Store<T>(T a) {
List<T> aList = Read<T>();
if (aList == null) {
aList = new List<T>();
}
aList.Add(a);
WriteNodes(aList);
}
public List<T> Read<T>() {
//Read a or b depend on T
}
Yes, you can do it by introducing common interface for items to be sotred and extract item type into enum.
STORAGE
public class Storage
{
public Storage()
{
// create it once on construction stage
// so you do not need to check for null each time in Sore()/Read()
this.AllItems = new List<IItem>();
}
public IList<IItem> AllItems { get; private set; }
public void Store<TItem>(TItem item)
where TItem: IItem
{
this.AllItems.Add(item);
}
public IEnumerable<IItem> Read(StorageItemType itemType)
{
return this.AllItems.Where(item => item.ItemType == itemType);
}
}
Abstract Storage Item type (more generic solution):
// Item types
enum StorageItemType
{
A,
B
}
interface IItem
{
int UID { get; }
StorageItemType ItemType { get; }
}
public abstract class StorageItemBase: IItem
{
public int UID { get; private set; }
public abstract StorageItemType ItemType
}
public sealed class B : StorageItemBase
{
public override StorageItemType ItemType
{
get
{
return StorageItemType.B; // !!!
}
}
}
Related
Hi I have created few nested iEnumerable implemented classes.
CDSWorkflowCollection
CDSModuleCollection
CDSSystemCollection
Below are my Classes
public class cdssystems
{
public string cdsSystemName;
public CDSModuleCollection listModules;
}
public class cdsmodules
{
public string moduleName;
public CDSWorkflowCollection listWorkflows;
}
class cdsdelgate
{
private string delgateName;
private DateTime fromDate;
private DateTime toDate;
private string functionElement;
private CDSSystemCollection cdsSystemsList;
private string cdsComments;
private string JobTitle;
}
public class cdsworkflows
{
public string WorkflowName;
public string ActionGroup;
}
class CDSWorkflowCollection : ICollection, IEnumerable<cdsworkflows>
{
private List<cdsworkflows> cdsWorkflowList;
private readonly object syncObject = new object();
public CDSWorkflowCollection(IEnumerable<cdsworkflows> cdsWorkflowList)
: base()
{
this.cdsWorkflowList = new List<cdsworkflows>(cdsWorkflowList);
}
public IEnumerator<cdsworkflows> GetEnumerator()
{
return this.cdsWorkflowList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.cdsWorkflowList.GetEnumerator();
}
public void CopyTo(Array array, int index)
{
if ((array.Rank != 1) || ((this.Count + index) > array.Length))
{
throw new ArgumentException();
}
int count = 0;
foreach (cdsworkflows cssWorkflow in this.cdsWorkflowList)
{
array.SetValue(cssWorkflow, count++);
}
}
public int Count
{
get
{
return this.cdsWorkflowList.Count;
}
}
public bool IsSynchronized
{
get
{
return false;
}
}
public object SyncRoot
{
get
{
return this.syncObject;
}
}
}
class CDSSystemCollection : ICollection, IEnumerable<cdssystems>
{
private List<cdssystems> cdsSystemList;
private readonly object syncObject = new object();
public CDSSystemCollection(IEnumerable<cdssystems> cdsSystemList)
: base()
{
this.cdsSystemList = new List<cdssystems>(cdsSystemList);
}
//Rest of the code here
}
class CDSDelegateCollection : ICollection, IEnumerable<cdsdelgate>
{
private List<cdsdelgate> cdsDelegateist;
private readonly object syncObject = new object();
public CDSDelegateCollection(IEnumerable<cdsdelgate> cdsDelegateList)
: base()
{
this.cdsDelegateist = new List<cdsdelgate>(cdsDelegateList);
}
//Rest of the code here
}
No i want to add objects to the class using group by and my code goes like below
var results = (from SPListItem item in myItemsList
group item by item["Systems"]
into grp
select new cdssystems()
{
cdsSystemName = grp.Key.ToString(),
listModules = (from item in grp
group item by item["Modules"]
into grpModules
select new cdsmodules()
{
moduleName = grpModules.Key.ToString(),
listWorkflows = (from item in grpModules
group item by item["Workflows"]
into grpWorkflows
select new cdsworkflows()
{
WorkflowName = grpWorkflows.Key.ToString(),
ActionGroup = grpWorkflows.FirstOrDefault()["ActionGroup"].ToString()
}
).ToList()
}).ToList()
}).ToList();
I am getting Error in ToList() saying
Cannot implicitly convert type 'System.Collections.Generic.List' to 'CDS.BusinessObjects.CDSWorkflowCollection'
I know the parm expects a collection and i am passing List. How to pass the collection. Please help
Delete your custom collection types and use the Generic ones from the .net framework instead. You can use List<T>, IList<T>, or ICollection<T>. There is almost never a need to write your own collection implementation(s).
public class cdsmodules
{
public string moduleName;
public CDSWorkflowCollection listWorkflows;
}
becomes
public class cdsmodules
{
public string moduleName { get; set; }
public List<cdsworkflows> listWorkflows { get; set; }
}
You should also follow proper visibility guidelines by never exposing fields directly. Instead use a property with a get;set; accessor. Fields should almost always be scoped as private or protected.
In C#, what is the best way to access a property of the derived class when the generic list contains just the base class.
public class ClassA : BaseClass
{
public object PropertyA { get; set; }
}
public class ClassB: BaseClass
{
public object PropertyB { get; set; }
}
public class BaseClass
{
}
public void Main
{
List<BaseClass> MyList = new List<BaseClass>();
ClassA a = new ClassA();
ClassB b = new ClassB();
MyList.Add(a);
MyList.Add(b);
for(int i = 0; i < MyList.Count; i++)
{
//I would like to access PropertyA abd PropertyB from the derived classes
}
}
Certainly you can downcast, like so:
for (int i = 0; i < MyList.Count; i++)
{
if (MyList[i] is ClassA)
{
var a = ((ClassA)MyList[i]).PropertyA;
// do stuff with a
}
if (MyList[i] is ClassB)
{
var b = ((ClassB)MyList[i]).PropertyB;
// do stuff with b
}
}
... However, you should take another look at what you're trying to accomplish. If you have common code that needs to get to properties of ClassA and ClassB, then you may be better off wrapping access to those properties up into a shared, virtual property or method in the ancestor class.
Something like:
public class BaseClass
{
public virtual void DoStuff() { }
}
public class ClassA : BaseClass
{
public object PropertyA { get; set; }
public override void DoStuff()
{
// do stuff with PropertyA
}
}
public class ClassB : BaseClass
{
public object PropertyB { get; set; }
public override void DoStuff()
{
// do stuff with PropertyB
}
}
Further to TimJ's answer, you can write one extension method that will work for all types:
public static IEnumerable<T> OfType<T>(this IEnumerable list)
{
foreach (var obj in list)
{
if (obj is T)
yield return (T)obj;
}
}
Or if you have Linq, that function is in the namespace System.Linq.
BaseClass o = MyList[i];
if (o is ClassB)
{
object k = ((ClassB)o).PropertyB;
}
if (o is ClassA))
{
object j = ((ClassA)o).PropertyA;
}
If you are doing this a lot, another option would be to create an extension method on the list to give you back the correctly typed enumeration. i.e.
public static class MyBaseListExtensions
{
public static IEnumerable<ClassA> GetAllAs(this List<MyBaseClass> list)
{
foreach (var obj in list)
{
if (obj is ClassA)
{
yield return (ClassA)obj;
}
}
}
public static IEnumerable<ClassB> GetAllbs(this List<MyBaseClass> list)
{
foreach (var obj in list)
{
if (obj is ClassB)
{
yield return (ClassB)obj;
}
}
}
}
Then you could use it like....
private void button1_Click(object sender, EventArgs e)
{
ClassA a1 = new ClassA() { PropertyA = "Tim" };
ClassA a2 = new ClassA() { PropertyA = "Pip" };
ClassB b1 = new ClassB() { PropertyB = "Alex" };
ClassB b2 = new ClassB() { PropertyB = "Rachel" };
List<MyBaseClass> list = new List<MyBaseClass>();
list.Add(a1);
list.Add(a2);
list.Add(b1);
list.Add(b2);
foreach (var a in list.GetAllAs())
{
listBox1.Items.Add(a.PropertyA);
}
foreach (var b in list.GetAllbs())
{
listBox2.Items.Add(b.PropertyB);
}
}
The whole premise doesn't make sense - what would PropertyB be for the a instance?
You can do this if you do manual runtime type checking (inst is Foo), and then cast to the type with the property you want.
You might have some issues with Generics and subclasses (in which case you should go back to System.Collections.ArrayList), but you have to cast the BaseClass to the subclass you wish to use. If you use the 'as' directory, it will succeed if the BaseClass can be casted to the subclass, or it will be null if it cannot be cast. It would look something like:
for(int i = 0; i < MyList.Count; i++)
{
BaseClass bc = MyList[i];
ClassA a = bc as ClassA;
ClassB b = bc as ClassB;
bc.BaseClassMethod();
if (a != null) {
a.PropertyA;
}
if (b != null) {
b.PropertyB;
}
}
Also, I should mention that this smells a bit bad. This is the kind of code that indicates a poorly structured object heirarchy. In general, if you can't say a IS A BaseClass, your design is probably wrong. But, hope that helps!
You would need to have the properties be declared as virtual on the base class and then override them in the derived class.
Ex:
public class ClassA : BaseClass
{
public override object PropertyA { get; set; }
}
public class ClassB: BaseClass
{
public override object PropertyB { get; set; }
}
public class BaseClass
{
public virtual object PropertyA { get; set; }
public virtual object PropertyB { get; set; }
}
public void Main
{
List<BaseClass> MyList = new List<BaseClass>();
ClassA a = new ClassA();
ClassB b = new ClassB();
MyList.Add(a);
MyList.Add(b);
for(int i = 0; i < MyList.Count; i++)
{
// Do something here with the Property
MyList[i].PropertyA;
MyList[i].PropertyB;
}
}
You would either need to implement the property in the base class to return a default value (such as null) or to make it abstract and force all the derived classes to implement both properties.
You should also note that you could return different things for say PropertyA by overrideing it in both derived classes and returning different values.
I have a list of derived types I want to map to a list of another type. Each derived type maps differently to the new type.
Is there any pattern to do this without casting each types and that keeps the logic of the mapping outside of the type (in a factory).
Here's an example, I'd like to find an alternative to GetFlatSwitch, something like GetFlat :
class Program
{
static void Main(string[] args)
{
List<Base> list = new List<Base>() { new DerivedA() { A = "A" }, new DerivedB() { B = "B" }};
List<Flat> flatList = list.Select(x => Factory.GetFlat(x)).ToList(); // not working
List<Flat> switchFlatList = list.Select(x => Factory.GetFlatSwitch(x)).ToList(); // works, but casting each element
}
static class Factory
{
public static Flat GetFlat(Base baseObj)
{
return new Flat();
}
public static Flat GetFlat(DerivedA a)
{
return new Flat() { A = a.A };
}
public static Flat GetFlat(DerivedB b)
{
return new Flat() { B = b.B };
}
public static Flat GetFlatSwitch(Base baseObj)
{
switch (baseObj.MyType)
{
case MyTypeEnum.A:
DerivedA a = baseObj as DerivedA;
return new Flat() { A = a.A };
case MyTypeEnum.B:
DerivedB b = baseObj as DerivedB;
return new Flat() { B = b.B };
default:
return new Flat();
}
}
}
enum MyTypeEnum
{
A, B
}
abstract class Base
{
public abstract MyTypeEnum MyType { get; }
}
class DerivedA : Base
{
public override MyTypeEnum MyType
{
get
{
return MyTypeEnum.A;
}
}
public string A { get; set; }
}
class DerivedB : Base
{
public override MyTypeEnum MyType
{
get
{
return MyTypeEnum.B;
}
}
public string B { get; set; }
}
class Flat
{
public string A { get; set; }
public string B { get; set; }
}
}
Rather than a design pattern, it seems like you need something like AutoMapper:
AutoMapper is a simple little library built to solve a deceptively
complex problem - getting rid of code that mapped one object to
another. This type of code is rather dreary and boring to write, so
why not invent a tool to do it for us?
Using that library you configure type-to-type mappings so you can call a method to map a type to other type (for example mapper.Map<A, B>(a, b)).
public abstrct class Item
{
public string Name {get;set;}
}
public class Music : Item
{
public double Price {get;set;}
}
public class Game : Item
{
public string Image {get;set;}
}
public class Inventory
{
private IList<Item> _games;
private IList<Item> _musics;
public Inventory()
{
_games = new List<Item>();
_musics = new List<Item>();
}
public void Add<T>(T item) where T : Item
{
if(typeof(T) == typeof(Game))
{
_game.add(item);
}
if(typeof(T) == typeof(Music))
{
_muisc.add(item);
}
public List<T> GetCollection<T>() where T : Item
{
return (List<T>) _muiscs;
}
class Porgram
{
static void Main(string[] args)
{
Inventory inventory = new Inventory();
var music1 = new Music(){ Name ="aa", Price = 10};
var Music2 = new Music() { Name ="bb", price = 20 };
inventory.add(music1);
inventory.add(music2);
List<Music> myMusics = inventory.GetCollection<Music>();
}
The code will compile but it will throw exception when try to Call Get Collection method.
I am not sure really why? I am guess i am using generic incorrect.
A List<Item> cannot be cast to a List<Music>. While Music is a subclass of Item, generic types do not follow the same inheritance pattern as their collection type. The simplest way to fix your code would be to replace the cast in your GetCollection method with a call to the Linq extension method cast, followed by ToList. That said, I think your entire class could be redesigned to handle this sort of inheritence better.
So, your GetCollection method looks like this:
public List<T> GetCollection<T>() where T : Item
{
return _musics.Cast<T>().ToList();
}
Try this code:
public abstract class Item
{
public string Name { get; set; }
}
public class Music : Item
{
public double Price { get; set; }
}
public class Game : Item
{
public string Image { get; set; }
}
public class Inventory<E> where E : Item
{
private IList<E> _games;
private IList<E> _musics;
public Inventory()
{
_games = new List<E>();
_musics = new List<E>();
}
public void Add(E item)
{
if (typeof(E) == typeof(Game))
{
_games.Add(item);
}
if (typeof(E) == typeof(Music))
{
_musics.Add(item);
}
}
public List<E> GetCollection()
{
return _musics;
}
}
public class Program
{
public static void Main(string[] args)
{
Inventory<Item> inventory = new Inventory<Item>();
var music1 = new Music() { Name = "aa", Price = 10 };
var music2 = new Music() { Name = "bb", Price = 20 };
inventory.Add(music1);
inventory.Add(music2);
List<Item> myMusics = inventory.GetCollection();
}
}
You need to declare your Inventory class to be generic where it takes in a class that extend Item
Also: It looks like you wrote the code, and didn't copy and paste it... I don't know why you did that...
Just modify your GetCollection method as
public List <T> GetCollection<T>() where T :Item
{
if (typeof(T) == typeof(Game))
{
return _games.Cast<T>().ToList();
}
if (typeof(T) == typeof(Music))
{
return _musics.Cast<T>().ToList(); ;
}
return null;
}
I've written a CustomerCollection class, which implements the IEnumerable and IEnumerator interfaces. Now I want the CustomerCollection class object to be searchable by Where() And Find() function and also would like to get a List object of type Customer From the CustomerCollection class. Please help. Also, is the implementation of the interfaces right.
public class Customer
{
private int _CustomerID;
private string _CustomerName;
public Customer(int customerID)
{
this._CustomerID = customerID;
}
public int CustomerID
{
get
{
return _CustomerID;
}
set
{
_CustomerID = value;
}
}
public string CustomerName
{
get
{
return _CustomerName;
}
set
{
_CustomerName = value;
}
}
}
public class CustomerController
{
public ArrayList PopulateCustomer()
{
ArrayList Temp = new ArrayList();
Customer _Customer1 = new Customer(1);
Customer _Customer2 = new Customer(2);
_Customer1.CustomerName = "Soham Dasgupta";
_Customer2.CustomerName = "Bappa Sarkar";
Temp.Add(_Customer1);
Temp.Add(_Customer2);
return Temp;
}
}
public class CustomerCollection : IEnumerable, IEnumerator
{
ArrayList Customers = null;
IEnumerator CustomerEnum = null;
public CustomerCollection()
{
this.Customers = new CustomerController().PopulateCustomer();
this.CustomerEnum = Customers.GetEnumerator();
}
public void SortByName()
{
this.Reset();
}
public void SortByID()
{
this.Reset();
}
public IEnumerator GetEnumerator()
{
return (IEnumerator)this;
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)this;
}
public void Reset()
{
CustomerEnum.Reset();
}
public bool MoveNext()
{
return CustomerEnum.MoveNext();
}
public object Current
{
get
{
return (Customer)CustomerEnum.Current;
}
}
}
You can call Cast<Customer>() on your IEnumerable which will give you an IEnumerable<Customer>, or just implement IEnumerable<Customer> to begin with. LINQ is almost entirely hooked into IEnumerable<T>, not IEnumerable. Once you did that you'd get all the LINQ to objects goodness for free.
I recommend to use OfType<T>() instead of Cast<T>() because if your collection contains T1 and T2, collection.Cast<T1>() will throw an error while collection.OfType<T1>() will return IEnumerable<T1> contains only instances of T1 not T2
This will do what you want. Note, I have abstracted out the IEnumerable to make it reusable and reduced the complexity of all of the other classes.
//Write your Test first
public class Test
{
public void TestEnumerator()
{
var customers = new CustomerCollection();
var qry =
from c in customers
select c;
foreach (var c in qry)
{
Console.WriteLine(c.CustomerName);
}
//Create a new list from the collection:
var customerList = new List<Customer>(customers);
}
}
public abstract class MyColl<T> : IEnumerable<T>
{
protected T[] Items;
public IEnumerator<T> GetEnumerator()
{
foreach (T item in Items)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class Customer
{
public Customer(int customerID)
{
CustomerID = customerID;
}
public int CustomerID { get; set; }
public string CustomerName { get; set; }
}
public class CustomerController
{
public Customer[] PopulateCustomer() {
return new [] {new Customer(1) {CustomerName = "Soham Dasgupta"},
new Customer(2) {CustomerName = "Bappa Sarkar"}};
}
}
public class CustomerCollection : MyColl<Customer>
{
public CustomerCollection()
{
Items = new CustomerController().PopulateCustomer();
}
}
public class CustomerController
{
public List<Customer> PopulateCustomer()
{
List<Customer> Temp = new ArrayList();
Customer _Customer1 = new Customer(1);
Customer _Customer2 = new Customer(2);
_Customer1.CustomerName = "Soham Dasgupta";
_Customer2.CustomerName = "Bappa Sarkar";
Temp.Add(_Customer1);
Temp.Add(_Customer2);
return Temp;
}
}
public class CustomerCollection : List<Customer>
{
List<Customer> Customers = new List<Customer>();
public CustomerCollection()
{
this.Customers = new CustomerController().PopulateCustomer();
}
}
new List<Customer>(myCustomerEnumerator);
The recommended base class to use for creating your own collection implementations is System.Collections.ObjectModel.Collection<T>
(from MSDN)
This base class is provided to make it easier for implementers to create a custom collection. Implementers are encouraged to extend this base class instead of creating their own.
public class CustomerCollection : Collection<Customer>
{
}