I am working on a large-scale checkout application for a current project.
This checkout has many cases depending on the user's admin level, how they got to the checkout, and what type of item they are checking out, and so the process is abstracted away from the .aspx pages via a set of context classes.
These classes all subclass from a single class, CheckoutContext, and the type of class to be used is noted via an enum.
Is there something similar to typedef I can use to choose which subclass to use, or should I simply have a method that returns the relevant class, like so:
CheckoutContext chooseSubclass(CheckoutCase c)
{
CheckoutContext output;
switch (c):
{
case CheckoutCase.SingleItemNew:
output = new SingleItemNew;
break;
case . . .
return output;
}
}
What you're implementing is a Factory Pattern. This is a standard practice, though it typically means writing a lot of repetitive code (much like your switch statement, which is often how they're implemented). You could do all sorts of fancy things like dynamic instantiation via reflection, but don't play with fire. Just stick with switch statement and you'll be fine.
If there are a large number of cases, I would create a Dictionary<CheckoutCase, Type> and populate it one time with the set of all CheckoutCase values and corresponding CheckoutContext Types. Then you could use Activator.CreateInstance to return the appropriate type instead of a gigantic switch statement.
You could implement this with a custom attribute and a factory method. Make all you Sub Classes implement a CustomAttribute say CheckOutCaseScenarioAttribute that takes the CheckOutCase Enum value.
Within your factory method, look for types that have this Enum Value set and create the object. This will avoid your switch case. This will work if you dont have any other initialization logic within your factory method.
This is called a Factory Design Pattern. I would create a static method that returns the needed class. A good practice here is to implement an Interface and return the interface.
interface ICheckoutItem
{
void CheckOut();
}
Have your items implement the interface. Then in your factory method return the interface of each item.
ICheckoutItem chooseSubclass(CheckoutCase c)
{
ICheckoutItem output;
switch (c):
{
case CheckoutCase.SingleItemNew:
output = new SingleItemNew;
break;
case . . .
return output;
}
}
You can create an attribute that has one property which would be the type of CheckoutContext:
public class CheckoutContextAttribute : Attribute
{
public Type CheckoutType{get;set;}
}
Then, on your enum, you can put the correct attribute on the correct enum type:
public enum CheckoutCase
{
[CheckoutContext(CheckoutType=typeof(SingleItemNew)]
SingleItemNew,
...
...
}
Then, in that method where you need to send back the correct Context type you use reflection and do something like this:
public CheckoutContext GetContext(CheckoutCase c)
{
FieldInfo field = c.GetType().GetField(c.ToString());
object[] attribs = field.GetCustomAttributes(typeof(CheckoutContextAttribute),false);
CheckountContext result = null;
if(attribs.Length > 0)
{
CheckoutContextAttribute attrib = attribs[0] as CheckoutContextAttribute;
Type type = attrib.CheckoutType;
result = Activator.CreateInstance(type) as CheckountContext;
}
return result;
}
This should do the trick. Just add some null / error checking to be safe.
Related
I have a situation where I'm receiving an object that can only ever be one of two known types - let's call them 'typeA' and 'typeB'. These two types are not related to each other.
I need to find the correct type before I can use the object. Here's some code showing my initial attempt to find the proper type:
public void OneOfTwoTypes(object obj)
{
try
{
var objType = (typeA)obj;
}
catch (InvalidCastException)
{
var objType = (typeB)obj;
}
// extra code for doing things to this object.
}
The above code wouldn't run because of scoping. And I couldn't declare var as a null either. So I went with dynamic instead:
public void OneOfTwoTypes(object obj)
{
dynamic objType;
try
{
objType = (typeA)obj;
}
catch (InvalidCastException)
{
objType = (typeB)obj;
}
// extra code for doing things to this object.
}
This runs fine and does the job. My question is - is there a better way of doing this ? Also, not having used dynamic before, are there any major issues with my current implementation ?
Edit: Adding a bit of commentary around what the object is being used for.
The typeA and typeB objects implement similar properties but represent different physical entities (one represents applied pressure while the other represents point loadings). For the code above, I will be using properties that are very similar (e.g. typeA.propertyA represents pressure loads from snow and is conceptually similar as typeB.propertyA in that both are from snow loading but the latter refers to point loads instead. Note that the two do have some properties that are different).
The end output will be a calculation for load combinations - which will include the same sort of calculations regardless of whether typeA or typeB is being used.
I don't want to use method overloads as that will involve significant amounts of copying of the same code for both object types (we're talking hundreds of lines here). I do like the idea of an interface which both types implement.
First, c# isn't really build to pass objects around as objects. one of the biggest benefits of a strongly typed language is that there aren't any surprises it terms of getting an array object when I want a list, or vise versa. You're first option should be a shared interface that both can implement that you can pass around as the interface instead of the explicit implementation.
Sometime that doesn't always work, though. and in that case you do still have options.
A language like c# also knows the object type in runtime. The basic api for this is obj.GetType() == typeof(TypeA), but that only evaluates to true if the type is the same and does not consider inheritance relationships or interface impelmentations. The starting point would then be obj is TypeA, which returns true if object can be assigned the type TypeA, else false. This can be used in an if statement as well (see below).
You can also use a pattern matching switch from C# 8, if you need to test more that one or two types.
// we'll pretend for this example that array and list of 2 completely different objects that need to be handled in code in completely different ways
var obj = new Random().Next(2) == 1
? (IEnumerable<Guid>) new []{ Guid.NewGuid() }
: new List<Guid> { Guid.NewGuid() };
// option 1: if statements with `is` keyword
if (obj is List<Guid> list)
{
// the variable list in in scope in the if block, since it was declared in the if statement
HandleList(list);
}
else if (obj is Guid[] array)
{
// the variable array is in scope in the else block
HandleArray(array);
}
// option 2: pattern matching switch
switch (obj)
{
case List<Guid> list:
HandleList(list);
break;
case Guid[] array:
HandleArray(array);
break;
default:
throw new ArgumentOutOfRangeException();
}
I'm learning the power of generics in C# in conjunction with NHibernate. I'd like to attempt the following in the pasted code.
In an attempt to do some post processing of N number of NHibernate objects I worked on a utility method leveraging generics to make it applicable to all NHibernate mapping classes we use now, or in the future. It works but I need to hard code each call for each mapping class. This is a pain and will need continuing updating as our schema and mappings change over time.
I do have an ever up-to-date list of all mapping classes by string name through the NHibernate mappings I generate on the fly. If there was a way to use this list of string names to call my generics based method, I'd be super happy.
Can anyone tell me if this is possible? Do I need to find another route?
Thanks so much in advance!!!
public static void ProcessSomeItems()
{
// *************************************************************
// As of now I have to list all classes as such to be processed
// It works but I have to update manually when new mapping classes are created
// *************************************************************
NHibDoSomethingUtil<AspnetMembership>();
NHibDoSomethingUtil<AspnetProfile>();
NHibDoSomethingUtil<AspnetRole>();
NHibDoSomethingUtil<AspnetUser>();
// and so forth...
// I have a up-to-date list of all mappings from "HbmMapping" and can get a list of all in the
// list form as below
List<string> mappingNames = new List<string>();
foreach (string mappingName in mappingNames)
{
Type theType = Type.GetType(mappingName);
// I know I'm getting Types and Generics classes and so forth all jumbled but
// how in the heck would I do something like the below?
NHibDoSomethingUtil<theType>(); // Obviously doesn't compile ;-)
}
}
// Generic method
public static void NHibDoSomethingUtil<T>() where T : class
{
using (ISession session = sourceDBSessionFactory.OpenSession())
{
foreach (dynamic item in new List<T>(session.QueryOver<T>().List()))
{
// Process item;
}
}
}
ecsousa gave great input and I was able to accomplish what I needed with something like the following.
foreach (HbmClass mappingClass in mapping.Items)
{
Console.WriteLine(" -- Discovered Mapping: " + mappingClass.Name);
Type mappingClassType = Type.GetType(mappingClass.Name);
var genericMethod = typeof(Migration).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(mappingClassType);
method.Invoke(null, null);
}
You will need to use Reflection in order to accomplish this. Instead of directly calling NHibDoSomethingUtil, try this:
var genericMethod = typeof(TheClassName).GetMethod("NHibDoSomethingUtil");
var method = genericMethod.MakeGenericMethod(theType);
method.Invoke(null, null);
Note that you have to replace TheClassName by the class containing both methods.
Keep in mind the this kind of code is slow, and you should use it very carefully.
I have a request to read string messages from a queue and "process them". Each message has a 4 digit "identifier/key" a the start, followed by date, time and another number...from then on, each message is different and requires different processing.
My thought was to use a factory to create an object of the required type and ALSO call the asbtract constructor at the same time.
Is this a sensible approach to take?
If so...how?
e.g.
1000,2013-02-13,09:00:00,492,....................
4000,2013-02-13,09:00:01,492,....................
1000,2013-02-13,09:00:02,74664,....................
4003,2013-02-13,09:00:03,1010,....................
4000,2013-02-13,09:00:04,493,....................
To build object of classes
Message1000 : AbstractMessage, IMessageThing
Message4000 : AbstractMessage, IMessageThing
Message4003 : AbstractMessage, IMessageThing
Where AbstractMessage contains a default constructor and properties for key, date, time, number etc.
If it makes sense depends on your requirements.
You could analyse the string like this:
// inside your actual factoryMethod...
var lines = ...;
foreach(var line in lines)
{
var tokens = line.Split(',');
// for split: you can also specify the max. amount of items if the ..... part can
// consist of more the dots.
CreateMessageObject(tokens); // eventually add to list of AbstractMessage or whatever
}
static FactoryClassConstructor()
{
_typeMap = new Dictionary<string, Type>();
_typeMap.Add("Message1000", typeof(Message1000));
// todo: add other message types
// you also could write a method which will use the class name of the
// type returned by typeof(XYZ) to assure the correct value as key
}
private Dictionary<string, Type> _typeMap;
private AbstractMessage CreateMessageObject(string[] tokens)
{
// simple error checking
if(tokens.Count != 5)
// todo: error handling
return null;
var type = typeMap[tokens[0]];
var instance = Activator.CreateInstance(type);
instance.Date = DateTime.Parse(tokens[1]);
instance.Time = DateTime.Parse(tokens[2]);
// todo initialize other properties
}
Of course you still need to do some error handling but I hope I could give you a good starting point.
The reason why i would use a dictionary is performance. Activator.CreateInstance is not very fast and the lookup with Type.GetType is also slow.
Instead of using the type as Value in the Dictionary you could also use something like this:
Dictionary<string, Action<IMessageThing>> _factories;
_factories = new Dictionary<string, Action<IMessageThing>>();
_factories.Add("Message1000", () => new Message1000());
and to create your object you could call:
var instance = _factories["Message1000"]();
Yes you can and is a correct and sensible approach. The things change a little if you can have a default constructor or not, and changes too if constructor will differ from one concrete implementation to the other. The simplest approach is to have a parameterless constructor.
With this prerequisite you can have something like this:
Type t = Type.GetType(string.Format("Handlers.MyHandlers.Message{0}",messageType));
var handler = Activator.CreateInstance(t) as IMessageThing;
In order to pass the string to the message, you can have a function defined in the IMessageThing interface, lets call it Init that you call immediately after the message creation, or probably better, have a costructor taking a string in the AbstractMessage class, and call it in the activator like this:
var handler = Activator.CreateInstance(t,body) as IMessageThing;
In the constructor of AbstractMessage call an abstract function Init(string body), so each concrete message need to implement its own parser.
Add some more error handling, and you have done.
One way is to split the string on , but set the max count to say 5, this should group all the values AFTER the number as one value:
var parts = your_string.split(new char[] {','},5);
Then you just need to use Activator.CreateInstance() to create your message instance. For example:
Type type = Type.GetType(String.Format("Message{0}",parts[0]));
var instance = Activator.CreateInstance(type) as IMessageThing;
You can then fill out the rest of the properties from parts.
You can either pass each message to a handler, the handler will check if it can handle this type of message. If so it will parse and return some object, otherwise it will return e.g. null and you will know to ask a different handler.
Or you build a parser that knows about the initial part that follows a common format, and then use a lookup table to find a specific message handler that will parse the remaining message and instantiate the correct type. (Pass the common parts to its constructor).
I don't understand what you mean with "create an object of the required type and ALSO call the asbtract constructor". There is no such thing as an abstract constructor. If you mean a constructor of an abstract base class, it is inevitable that it will get called when a subclass is instantiated.
SlovenianSearchQueryManager.ApplySloveniaQueries(rawQuery, page, pageSize, orderBy, out count);
is it possible to get SlovenianSearchQueryManager class by concatinating HttpContext.Current.Session["lang_name"] and "SearchQueryManager";
HttpContext.Current.Session["lang_name"] is Slovenian so together is SlovenianSearchQueryManager
next time
HttpContext.Current.Session["lang_name"] is German so together is
GermanSearchQueryManager
i try to make this generic instead of making
if (HttpContext.Current.Session["lang_name"] == "Slovenian)
{
SlovenianSearchQueryManager.ApplySloveniaQueries(rawQuery, page, pageSize, orderBy, out count);
}
else ...
i want to make something like
(HttpContext.Current.Session["lang_name"] + "SearchQueryManager").ApplySloveniaQueries(...
is this even possible to make something like this and the type SlovenianSearchQueryManager for example exists, it's in a different class library.
You should make an ISearchQueryManager interface with implementations for each language, then use a Dictionary<string, ISearchQueryManager> to get the implementation for the desired language.
Use Type.GetType and Activator.CreateInstance:
var queryManagerType = Type.GetType(HttpContext.Current.Session["lang_name"] + "SearchQueryManager");
var queryManager = Activator.CreateInstance(queryManagerType);
Activator.CreateInstance returns an object. You might want to define an interface that you query managers will need to implement.
public interface ISearchQueryManager {
void ApplyQueries(/* add parameters here */);
}
Then you can cast the object returned by CreateInstance to the interface:
var queryManagerType = Type.GetType(HttpContext.Current.Session["lang_name"] + "SearchQueryManager");
var queryManager = (ISearchQueryManager)Activator.CreateInstance(queryManagerType);
queryManager.ApplyQueries(rawQuery, page, pageSize, orderBy, out count);
I would recommend making two changes.
First, make all of your "SearchQueryManager" instances derive from a common interface (or base class), such as ISearchQueryManager.
Second, make a Dictionary<string, ISearchQueryManager>, and use it to lookup the appropriate one based on your type.
You could then do:
ISearchQueryManager manager;
if (!managers.TryGetValue(HttpContext.Current.Session["lang_name"], out manager))
{
// Unknown language - handle this!
}
else
{
manager.ApplyQuery(...);
}
You're looking at the factory pattern. Define an interface, like IQueries with the queries methods you need. Then define a factory class that takes your language and returns an IQueries. For slovenia, return your SlovenienSearchQueryManager. Etc for other languages.
It sounds like you're trying to work around creating a factory pattern, which would, IMO, be the correct way to solve this problem.
http://aspalliance.com/809
I would investigate using a factory class to create your query managers.
Pseudo-code:
var factory= new QueryManagerFactory();
var queryManager = factory.Create("Slovenian");
In your factory create method return the type of query manager you want based upon the input.
if you take a look at the following code, you will (hopefully) see what I am trying to archieve. Basically this code does:
A query for generic storag items (they store their type as string)
If the item is a subclass of SearchCriteria, create the correct instance
Add the instance to the list (SearchCriteria is superclass)
Not very elegant is, of course, the pseudo-switch case, which I would have to update for all different criteria I create.
So, my question, is there a "generic" way to create an instance which is strongly typed using a string as "source" for the type.
I know I can use Reflection to create an instance, but this is of type object, so I would not be able to add it to the list. Oh, just got an idea... Create object using reflection, cast it to supertype (SearchCrit), add to list. Real type should still be the "correct subtype" I hope...
Will try it, and update this post with results. Any better ideas?
Chris
private IList<SearchCriteria> _searchCriteriaAll;
public IList<SearchCriteria> SearchCriteriaAll
{
get
{
if (_searchCriteriaAll == null)
{
_searchCriteriaAll = new List<SearchCriteria>();
var tN = typeof (SearchCriteria).ToString();
foreach (var o in DataStorage.LinkedObjects)
{
if (tN.StartsWith(o.TypeName))
{
if (o.TypeName == typeof(StringSearchCriteria).ToString())
_searchCriteriaAll.Add(new StringSearchCriteria(o));
}
}
}
return _searchCriteriaAll;
}
}
EDIT:
Thanks for the tips, the "correct" way would definitly be the factory pattern. I will look into that. For now, I use this hack, because the subclasses are so small, I dont want a factory for each one.. (and this place is currently the only one with such a "fancy" feature)
private IList<SearchCriteria> _searchCriteriaAll;
public IList<SearchCriteria> SearchCriteriaAll
{
get
{
if (_searchCriteriaAll == null)
{
_searchCriteriaAll = new List<SearchCriteria>();
var tN = typeof (SearchCriteria).ToString();
foreach (var o in DataStorage.LinkedObjects)
{
if (tN.StartsWith(o.TypeName))
{
var newO = Activator.CreateInstance(typeof(SearchCriteria).Assembly.FullName, o.TypeName);
var newCrit = newO.Unwrap() as SearchCriteria;
newCrit.DataStorage = o;
_searchCriteriaAll.Add(newCrit);
}
}
}
return _searchCriteriaAll;
}
}
Generics and reflection don't make good friends. A simpler approach here is to use the non-generic list interface:
_searchCriteriaAll = new List<SearchCriteria>();
IList list = (IList) _searchCriteriaAll;
...
Type type = typeof(SearchCriteria).Assembly.GetType(o.TypeName);
list.Add(Activator.CreateInstance(type));
(where o.TypeName includes the namespace information, but doesn't have to be assembly-qualified)
This is still runtime type-safe (it'll throw at runtime if it is wrong), and still adjusts the same list.
Note also that we only look inside Assembly directly via Assembly.GetType().
I'd say you're looking for the Factory Method Pattern.
There's a C# sample here - the first link explains the pattern better, the second is the right language for you.
It's not entirely clear to me what you are trying to achieve, but you can create a Type from a string like this:
var t = Type.GetType(typeName);
If you want to examine whether it's a proper subtype, you can use the IsAssignableFrom method.