I have different types of item. Each of them has a enum where ID's are keept. I want my base class to have a method to check if the item's ID is on a given list. Something like:
abstract class Thing
{
public string Name;
public int Amount;
//others
abstract bool IsInList(list<xxxxx> list);
abstract xxxxxxx ID;
}
class Fruit : Thing
{
public IDs = {F_NotSet, F_Banana, F_Apple, ...}
public IDs ID = IDs.F_NotSet;
public bool IsInList(List<T> list) //this wont compile
{
if(typeof(T) == typeof(IDs))
return list.Contains(IDs);
else
return false;
}
}
The thing is that I also have a (blazor) UI component that visualizes things so I want to be able to do my
<div>#Thing.Name [#Thing.Amount]</div>
<code>
[Parameter] public Thing Thing {get;set;}
</code>
And use it on my page for all kind of things, like :
<div>
#foreach(var thing in Things) //things being List<Thing>
{
<ThingViewer ItemToShow=thing/>
}
</div>
That's why I don't want to go the Thin<T>path because then my UI component to visualize Things and my page gets messy.
On the other side, I would like to use this "IsInList" method from the page to do things like
<div>
#foreach(var thing in MyThings) //MyThings being List<Thing>
{
#if(thing.IsInList(ThingsInOffer))
{
<div class="offer">
<ThingsVisualizer ItemToShow=thing/>
</div>
}
}
</div>
Here is the complete working example from fiddle, that helped solve the issue:
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;
public class Program
{
// Just for showcasing:
public static void Main()
{
var apple = new Fruit(Fruits.Apple);
var banana = new Fruit(Fruits.Banana);
var orange = new Fruit(Fruits.Orange);
var strawberry = new Nut(Nuts.Strawberry);
var assortedFruits = new []{ Fruits.Apple, Fruits.Orange };
Console.WriteLine("I ({0}) am {1}an assorted fruit!", apple.Id, apple.IsInList(assortedFruits)?"":"not ");
Console.WriteLine("I ({0}) am {1}an assorted fruit!", banana.Id, banana.IsInList(assortedFruits)?"":"not ");
Console.WriteLine("I ({0}) am {1}an assorted fruit!", strawberry.Id, strawberry.IsInList(assortedFruits)?"":"not ");
PrintDetails(apple);
PrintDetails(banana);
PrintDetails(orange);
PrintDetails(strawberry);
foreach( var thing in new IThing[]{apple, strawberry} )
{
Console.WriteLine($"{thing.Name}s have {thing.SomeProperty}");
}
}
public static void PrintDetails(IThing thing)
{
// Going by an interface: We do not really care, what kind of
// "thing" it is in particular.
thing.Print();
}
}
// Two "Kinds" of Things: Fruits and Nuts
public enum Fruits
{
Apple,
Banana,
Orange
}
public enum Nuts
{
Strawberry
}
// Things all kinds of "Things" need to be able to do:
public interface IThing
{
void Print();
string Name {get;}
string SomeProperty {get;}
}
// Common generic implementations:
public abstract class Thing<TIdentifyingThing> : IThing
{
protected TIdentifyingThing _me;
protected Thing(TIdentifyingThing id)
{
_me = id;
}
public TIdentifyingThing Id => _me;
public string Name => _me.ToString();
public abstract string SomeProperty {get;}
// I think the "thing" here was that you can have generic methods
// with Types that do not need to be the same as the generic class's
// type. Here `T` versus `TIdentifyingThing`.
public bool IsInList<T> (IEnumerable<T> list)
{
if( typeof(T) != typeof(TIdentifyingThing) ) return false;
if( list is not null && list.Any() )
{
return list.Cast<TIdentifyingThing>().Contains(_me);
}
return false;
}
public abstract void Print();
}
// The specific Things:
public class Fruit : Thing<Fruits>
{
public Fruit( Fruits identity ): base(identity) {}
public override string SomeProperty => "just some property.";
public override void Print()
{
Console.WriteLine($"My fruity details are: I am a{(Id.ToString().StartsWithVocal() ? "n" : "" )} {Id}.");
}
}
public class Nut : Thing<Nuts>
{
public Nut( Nuts identity ): base(identity) {}
public override string SomeProperty => "not always the appearance you expect.";
public override void Print()
{
Console.WriteLine($"My nutty details are: I am a{(Id.ToString().StartsWithVocal() ? "n" : "" )} {Id}.");
}
}
// Just so the code is complete. Doesn't actually contribute to the solution as such.
public static class StringExtensions
{
public static bool StartsWithVocal(this string me)
{
return Regex.IsMatch(me, "^[aeiouAEIOU]");
}
}
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.
I have been facing a challenge, questioning my faith in OOP. Kindly let me know how if this is possible:
I have a parent class with a Static List (to keep track of all objects created, mainly for UI DataGrid reasons) and a Method referring to that List. Something like that
abstract class Animal
{
public static List<Animal> objList;
public String Name;
public Animal(String Name)
{
this.Name = Name;
objList.Add(this);
}
public virtual void delete(int i)
{
objList.RemoveAt(i);
}
now I have a child class with also a static list (same name same purpose just different class) but in order to have the method referring to the child.list I have to rewrite the method. like that
class Cat : Animal
{
public static List<Cat> objList;
public Cat(String Name) : base(Name)
{
}
//whould it be possible to ommit this method?
public override void delete(int i)
{
objList.RemoveAt(i);
}
}
This cannot be the best way. If I would have 5 children they would all have the same part of code copy pasted.
There must be a way that the parent class "delete" method, if called from a child object, it deletes from the child list, not from the parent list.
Static properties and methods don't lend themselves to being overridden in an OOP manner, though they can be shadowed.
public class Parent : IDisposable
{
private static List<Parent> objList = new List<Parent>();
private static IReadOnlyList<Parent> readOnlyList = new ReadOnlyCollection<Parent>(objList);
public static IEnumerable<Parent> Instances { get { return readOnlyList; } }
private bool _isDisposed = false;
public bool IsDisposed { get { return _isDisposed; } }
public Parent()
{
objList.Add(this);
}
public void Dispose()
{
OnDispose(true);
GC.SuppressFinalize(this);
}
protected virtual void OnDispose(bool disposing)
{
if(disposing) { objList.Remove(this); }
_isDisposed = true;
}
}
public class Child : Parent
{
private static IEnumerable<Child> _instances = Parent.Instances.OfType<Child>();
public new static IEnumerable<Child> Instances { get { return _instances; }}
public Child() : base()
{
}
}
Now if you want to remove the ith item from the list, just use use Parent.Instances(i).Dispose();
You can also remove the ith Child instance by doing Child.Instances(i).Dispose()
Edit: Finalizer removed from Parent as suggested in the comments below.
Edit2: Simplified the LINQ expression in the Child class to use .OfType() as suggested in the comments.
The collection does not belong in the base class, and certainly, it does not belong in any derived classes.
Unfortunately, you do not show how your List<Animal> is used so it is difficult to show a meaningful answer without inferring behavior you might not desire.
But if you insist, you need to have one collection only to hold all the animals and to declare a static property that filters the original collection according to the type in each subclass.
public abstract class Animal
{
// this is the _only_ field that should contain
// a list of all the animals.
protected static readonly List<Animal> animals = new List<Animal>();
// Expose a read-only wrapper as public
public static IReadOnlyList<Animal> AllAnimals => animals.AsReadOnly();
protected Animal(string color)
{
animals.Add(this);
this.Color = color;
}
public string Color { get; }
public void RemoveMe()
{
int index = animals.IndexOf(this);
if (index >= 0)
{
animals.RemoveAt(index);
}
}
}
public class Cat : Animal
{
public static IReadOnlyList<Cat> AllCats => animals.OfType<Cat>().ToList().AsReadOnly();
public Cat(string name, string color) : base(color)
{
this.Name = name;
}
public string Name { get; }
}
public class Fish : Animal
{
public static IReadOnlyList<Fish> AllFish => animals.OfType<Fish>().ToList().AsReadOnly();
public Fish(string color) : base(color)
{
}
}
static class Program
{
static void Main(string[] args)
{
var cat1 = new Cat("Whiskers", "Tabby");
var fish1 = new Fish("Striped");
var cat2 = new Cat("Snoflake", "White");
var cat3 = new Cat("Midnight", "Black");
cat2.RemoveMe();
// list all remaining cats below
foreach (var cat in Cat.AllCats)
{
Debug.WriteLine($"{cat.Name} is a {cat.Color} cat.");
}
// Result in Output:
//Whiskers is a Tabby cat.
//Midnight is a Black cat.
}
}
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.
I have the (pseudo) code:
public class GlobalClass
{
public GlobalClass()
{
var x = this.GetType().Name // Returns "Channels"
// WHAT TO DO HERE?
}
}
public class BaseClass
{
public string Title { get; set; }
}
And using this code:
public class Channels : GlobalClass
{
public Channels()
{
}
public class Channel : BaseClass
{
}
}
Where the comment is (// WHAT TO DO HERE?), I want to get the runtime type of BaseClass,
where in my sample code should return Channel.
I am open to different approaches, but only if it's accompanied with an explanation why I should change the code.
I think you need a generic class here, something like:
public class GlobalClass<T> where T : BaseClass
{
public GlobalClass()
{
var theType = typeof(T); //you got it
}
}
public class BaseClass
{
public string Title { get; set; }
}
public class Channel : BaseClass { }
public class Channels : GlobalClass<Channel> { }
You can use reflection like this:
using System.Reflection;
...
public class GlobalClass
{
public GlobalClass()
{
Type[] types = Assembly.GetExecutingAssembly ().GetTypes ();
foreach ( Type t in types )
{
if ( t.BaseType == typeof ( BaseClass ) )
{
Console.WriteLine ( "I found a class " + t.Name + " that subclass BaseClass" );
}
}
}
}
See also Stack Overflow question List of classes in an assembly.
is operator is just for that purpose.
getType() method with class Type can also be used.
class Example
{
static void ShowTypeInfo (object o)
{
Console.WriteLine ("type name = {0},
full type name = {1}", o.GetType(),
o.GetType().FullName );
}
public static void Main()
{
long longType = 99;
Example example= new Example();
ShowTypeInfo (example);
ShowTypeInfo (longType);
}
}
To get the runtime type of anything, you first need an object instance to get the type from. So with your given structure, that's not possible.
There are two possible approaches:
Add a BaseClass parameter to the constructor of your GlobalClass:
public class GlobalClass
{
public GlobalClass(BaseClass data)
{
var dataType = data == null ? null : data.GetType();
// do something with the type
}
}
public class Channels : GlobalClass
{
public Channels(Channel data) : base(data)
{
}
public class Channel : BaseClass
{
}
}
Pass the type to the constructor directly:
public class GlobalClass
{
public GlobalClass(Type actualType)
{
Debug.Assert(typeof(BaseClass).IsAssignableFrom(actualType));
}
}
public class Channels : GlobalClass
{
public Channels() : base(typeof(Channel))
{
}
public class Channel : BaseClass
{
}
}
If the structure for some reason doesn't allow generics here (as Danny Chen suggested), I'd personally prefer the second approach, since that doesn't need an actual instance.
I have my domain model with several NewsType's which are subclasses of NewsItem as shown below (simplified):
public abstract class NewsItem : Entity
{
public virtual Account Account { get; set; }
public virtual DateTime DateTime { get; set; }
}
Here are a couple of subclasses of NewsItem:
public class NewsItemJoiner : NewsItem
{
public virtual Account AccountJoined { get; set; }
}
public class NewsItemStatus : NewsItem
{
public virtual string Status { get; set; }
}
In my MVC app I want to return a collection of Newsitem's which may contain many different subclasses of NewsItem. Is what I now need to do is loop through each news item and call a Render function from the relevant class for that specific type of NewsItem...code might explain it a little easier:
public interface IRenderer<T> where T : NewsItem
{
string Render(T item);
}
public class JoinedRenderer : IRenderer<NewsItemJoiner>
{
public string Render(NewsItemJoiner item)
{
return String.Format("{0} has just joined our music network.", item.AccountJoined.ArtistName);
}
}
public class StatusUpdateRenderer : IRenderer<NewsItemStatus>
{
public string Render(NewsItemStatus item)
{
return String.Format("<span class='statusupdate'>{0}<span>", item.Status);
}
}
I need to somehow call the correct classes Render function depending on the type of NewsItem.
This seems like a rather obvious case for a virtual function.....
public abstract class RenderableNewsItem : NewsItem
{
abstract public string Render();
}
public class NewsItemStatus : RenderableNewsItem
{
public virtual string Status { get; set; }
public string Render()
{
return String.Format("<span class='statusupdate'>{0}<span>", this.Status);
}
}
You could make a Dictionary that used the type of NewsItem as a key and the Render function to be used as a value. Or, you could maintain a list of all of the classes with Render functions or just of all the Render functions and use Reflection to determine which method should be used. However, it seems to me that instead of doing any of this you should consider redesigning your application so that the NewsItem abstract class itself has a virtual Render function. This would greatly simplify your task.
Edit: Previously thought NewsItem was an interface.
One possibility: on startup (i.e. in a static constructor related to your rendering code), iterate through the classes in your assembly and instantiate and store a Dictionary<Type, object> of IRenderer<T>-implementing instances mapped to the type that they render.
(This suggestion assumes that the renderer objects are thread-safe, since you may end up calling the Render method from more than one request thread at one time. If they're not thread safe, then you'd need to change the dictionary to <Type, Type> and instantiate a renderer for each use.)
For example:
public class RenderUtil
{
static Dictionary<Type, object> s_renderers;
static RenderUtil()
{
s_renderers = new Dictionary<Type, object>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var renderInterface = type.GetInterfaces().FirstOrDefault(
i => i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRenderer<>));
if (renderInterface != null)
{
s_renderers.Add(
renderInterface.GetGenericArguments()[0],
Activator.CreateInstance(type));
}
}
}
public static string Render<T>(T item)
{
IRenderer<T> renderer = null;
try
{
// no need to synchronize readonly access
renderer = (IRenderer<T>)s_renderers[item.GetType()];
}
catch
{
throw new ArgumentException("No renderer for type " + item.GetType().Name);
}
return renderer.Render(item);
}
}
Usage:
var newsItem = new NewsItemStatus();
// in your example code, ends up calling StatusUpdateRenderer.Render:
var rendered = RenderUtil.Render(newsItem);
Note that the RenderUtil class will throw a DuplicateKeyException via a TypeInitializationException on first use if there is more than one renderer for a given type.
What I would do instead is have multiple partial views for rendering different NewsItem subclasses. Then, I would have some sort of mapping between the subclasses and the partial view names. Here's two ways to do this:
NewsItem could have a virtual string property/method that returns the name of the partial view associated with it. I'd only recommend this if NewsItem is a specifically a model class used to pass into views, not if it's an ORM class or similar.
In the model containing the list of news items, you could have a mapping property (a Dictionary<Type, string> for example).
Once you have this set up, your view could look something like this:
<% foreach (var newsItem in Model.NewsItems) { %>
<%= Html.RenderPartial(newsItem.PartialViewName, newsItem) %>
<% } >
I just did something like this, but I don't have the code handy. It used Reflection and looked like the following:
List<GenericContainer> gcList = new List<GenericContainer>();
// GenericContainer can be a Jug, Bottle, Barrel, or just a GenericContainer type
// [..fill it..]
GenericContainer gc = gcList[i];
Object returnvalue = gc.GetType()
.GetMethod("Pour", BindingFlags.Instance).Invoke(gc, null);
Consider inverting the control logic and providing a virtual Render() method in NewsItem instead. E.g.
abstract class NewsItem {
// ...
public virtual string Render() { return string.Empty; }
}
Then your subclass can implement as desired:
public class NewsItemJoiner : NewsItem
{
// ...
public override string Render() {
return String.Format("{0} has just joined our music network.", this.AccountJoined.ArtistName);
}
}
Edit:
Alternative Technique
Point taken about the comments from others re separation of concerns. I don't know if you're set on the IRenderer<T> for other reasons, but if you aren't, there's another technique which doesn't need to use reflection. You could use the Visitor pattern instead.
First you declare a NewsItemVisitor class:
public abstract class NewsItemVisitor
{
public abstract void Visit(NewsItemJoiner joiner);
public abstract void Visit(NewsItemStatus status);
}
Next, add a virtual Accept() method to NewsItem (for this example, I've changed your data types to string instead of Account, Status etc):
public abstract class NewsItem
{
public virtual string Account { get; set; }
public virtual DateTime DateTime { get; set; }
public abstract void Accept(NewsItemVisitor visitor);
}
public class NewsItemJoiner : NewsItem
{
public virtual string AccountJoined { get; set; }
public override void Accept(NewsItemVisitor visitor)
{
visitor.Visit(this);
}
}
public class NewsItemStatus : NewsItem
{
public virtual string Status { get; set; }
public override void Accept(NewsItemVisitor visitor)
{
visitor.Visit(this);
}
}
Now you can create a concrete Visitor, which is our renderer:
public class NewsItemListRenderer : NewsItemVisitor
{
private readonly List<NewsItem> itemList;
private string renderedList = string.Empty;
public NewsItemListRenderer(List<NewsItem> itemList)
{
this.itemList = itemList;
}
public string Render()
{
foreach (var item in itemList)
{
item.Accept(this);
}
return renderedList;
}
public override void Visit(NewsItemJoiner joiner)
{
renderedList += "joiner: " + joiner.AccountJoined + Environment.NewLine;
}
public override void Visit(NewsItemStatus status)
{
renderedList += "status: " + status.Status + Environment.NewLine;
}
}
Sample code for how to render a list of NewsItem instances:
List<NewsItem> itemList = new List<NewsItem>();
itemList.Add(new NewsItemJoiner { AccountJoined = "fred" });
itemList.Add(new NewsItemJoiner { AccountJoined = "pete" });
itemList.Add(new NewsItemStatus { Status = "active" });
itemList.Add(new NewsItemJoiner { AccountJoined = "jim" });
itemList.Add(new NewsItemStatus { Status = "inactive" });
NewsItemListRenderer renderer = new NewsItemListRenderer(itemList);
Console.WriteLine(renderer.Render());
Running this gives the following output:
joiner: fred
joiner: pete
status: active
joiner: jim
status: inactive