Switch based on generic argument type - c#

In C# 7.1 the below is valid code:
object o = new object();
switch (o)
{
case CustomerRequestBase c:
//do something
break;
}
However, I want to use the pattern switch statement in the following scenario:
public T Process<T>(object message, IMessageFormatter messageFormatter)
where T : class, IStandardMessageModel, new()
{
switch (T)
{
case CustomerRequestBase c:
//do something
break;
}
}
The IDE gives me the error "'T' is a type, which is not valid in the given context"
Is there an elegant way to switch on the type of a generic parameter? I get that in my first example you are switching on the object and the second I'd want to switch on the type T. What would be the best approach to do this?

Below are two different classes called Foo and Bar. You can use one instance of any of these classes as a parameter to a function named Process. After all, you can perform pattern matching as shown in the example function. There is a function named Test for the usage example..
public class Foo
{
public string FooMsg { get; set; }
}
public class Bar
{
public string BarMsg { get; set; }
}
public class Example
{
public T Process<T>(T procClass) where T : class
{
switch (typeof(T))
{
case
var cls when cls == typeof(Foo):
{
var temp = (Foo)((object)procClass);
temp.FooMsg = "This is a Foo!";
break;
}
case
var cls when cls == typeof(Bar):
{
var temp = (Bar)((object)procClass);
temp.BarMsg = "This is a Bar!";
break;
}
}
return
procClass;
}
public void Test(string message)
{
Process(new Foo() { FooMsg = message });
Process(new Bar() { BarMsg = message });
}
}

I agree that there are situation when this approach is faster and not so ugly, and also agree that in any case a better solution should be found, but sometimes the trade-off doesn't pay... so here is a solution (C# 9.0)
return typeof(T) switch
{
Type t when t == typeof(CustomerRequestBase) => /*do something*/ ,
_ => throw new Exception("Nothing to do")
};

I'm going to preface by saying that in general I agree with all the commenters that say switching on a generic T is probably not a good idea. In this case I would advice him to stick with identifying the object, casting it, and then passing it to an appropriate handler.
However, I've been writing a custom binary serializer that needs to be fairly efficient and I've discover one case where I feel the kind of switching (or if statement) he's asking for is justified, so here's how I managed it.
public T ProcessAs<T>(parameters)
{
if (typeof(T) == typeof(your_type_here)
{
your_type_here tmp = process(parameters);
return Unsafe.As<your_type_here, T>(ref tmp);
}
else if (typeof(T) == typeof(your_other_type))
{
your_other_type tmp = otherProcess(parameters);
return Unsafe.As<your_other_type, T>(ref tmp);
}
else
{
throw new ArgumentException(appropriate_msg);
}
}
Note that Unsafe.As<T>(T value) can be used if you're dealing with a class instead of a struct.

If we ignore the codesmell discussion as per comments, an easy readable implementation (hack) can look like this:
public T Process<T>(string number)
{
switch (typeof(T).FullName)
{
case "System.Int32":
return (dynamic) int.Parse(number);
case "System.Double":
return (dynamic) double.Parse(number);
default:
throw new ArgumentException($"{typeof(T).FullName} is not supported");
}
}
Even if you are # with your generic constraints, this is probably bound to cause issues unless you are the sole programmer ;)

Related

Return instance using reflection in C#

A sample code I tried to return an instance of class is given below.
public object getConstructorclass(int i)
{
if(i==1)
{
Type type = Type.GetType("test1");
}else
{
Type type = Type.GetType("test2");
}
return Activator.CreateInstance(type);
}
var objcls = getConstructorclass(1);
objcls.callclass();//error occured
How can I mention the class type here since the type is not known at compile time but it will decided at runtime.In the above example i just pass a value 1 (it can be anything and that class will be called accordingly), and the class test1 called.
here I will get an error on the line objcls.callclass(), because objcls is an object instance that doesn't have a callclass()method.
How can I restructure this piece of code? My aim is if I mention a class in the getConstructorclass() method, an object should be returned so as to use it in the further code to invoke the members of that class.
If you know that your classes will have this method, you should use a common interface for them and implement it accordingly. Then you will work with classes that you have made sure it will work.
It would look like this
IMyInterface objcls = getconstrorclass() as IMyInterface;
if (objcls != null)
objcls.callclass();
else
// we failed miserably and should do something about it
I don't think you should use some generic object returning constructor based on an int variable, if your classes don't have anything in common. It's really weird to handle it like this and it may lead to various problems (some of which you're currently already experiencing). Generic class constructors make sense if the classes are somewhat related and you can predict the outcome, but to create a do-it-all method.. Not so sure about correctness of such approach.
Anyway, if you insist (not recommended, but as you wish), you can create some checks for a type like this:
var createdObject = getConstructorclass(1);
if (createdObject is MyClass1)
{
var specificObject = (MyClass1)createdObject;
specificObject.callMethod1();
}
else if (createdObject is MyClass2)
{
var specificObject = (MyClass2)createdObject;
specificObject.callSomeOtherMethod();
}
...
But it gets very error prone soon, refactoring will probably be a nightmare etc., but it's your call..
Or you maybe can use solution from pwas, but to me it seems unnecessarily complicated for such a basic task. Looks nice and all, but it still returns only the type "object", so it doesn't really solve your specific problem.
Also, to address one issue I'm not sure you understand - you've already created the instance, you just return type object. That is why you can't call any specific methods on this object, because first you have to cast it to something, that actually has that method and make sure the cast can be done (inheritance etc).
If interface solution (see other answers) is enough, don't look at this answer. When you can't use common base class / interface and you still want call members, you can use solution with is keyword (and check types). Instead of writing many ifs for each case, you can use fluent API:
object obj = this.getConstructorclass();
obj.StronglyInvoke()
.When<int>(value => Console.WriteLine("Got {0} as int", value))
.When<string>(value => Console.WriteLine("Got {0} as string", value))
.OnFail(() => Debug.Write("No handle."))
.Invoke();
Solution:
public class GenericCaller
{
private IList<GenericInvoker> invokers = new List<GenericInvoker>();
private readonly object target;
private Action failAction;
public GenericCaller(object target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
this.target = target;
}
public GenericCaller OnFail(Action fail)
{
this.failAction = fail;
return this;
}
public GenericCaller When<T>(Action<T> then)
{
if (then == null)
{
throw new ArgumentNullException("then");
}
var invoker = new GenericInvoker<T>(this.target, then);
this.invokers.Add(invoker);
return this;
}
public void Invoke()
{
if (this.invokers.Any(invoker => invoker.Invoke()))
{
return;
}
if (this.failAction == null)
{
throw new InvalidOperationException("Handler not found");
}
this.failAction();
}
public abstract class GenericInvoker
{
protected readonly object target;
protected GenericInvoker(object target)
{
this.target = target;
}
public abstract bool Invoke();
}
public class GenericInvoker<T> : GenericInvoker
{
private readonly Action<T> then;
public GenericInvoker(object target, Action<T> then)
: base(target)
{
this.then = then;
}
public override bool Invoke()
{
if (this.target.GetType() == typeof(T))
{
this.then((T)this.target);
return true;
}
return false;
}
}
}
public static class Extensions
{
public static GenericCaller StronglyInvoke(this object o)
{
return new GenericCaller(o);
}
}
Remeber - it would be more elegant to use common interface (as other answers say) - my is only alternative way.
Declare your variable as dynamic
dynamic objcls = getconstrorclass();
Using this the will be determined at run-time, whatever the getconstrorclass method returns. You can access any member of the type and you won't get any error at compile-time. But if you try to access a member which doesn't exists you will get a RuntimeBinderException at runtime.
I would recommend using an interface and restricting the classes that you can instantiate this way to only those that implement the interface.
public interface IMyInterface
{
void callclass();
}
public <T> getConstructorClass()
{
T instance;
Type type = Type.GetType("test1");
// instance will be null if the object cannot be cast to type T.
instance = Activator.CreateInstance(type) as T;
return T;
}
IMyInterface objcls = getConstructorClass<IMyInterface>();
if(null != objcls)
{
objcls.callclass();
}
not sure what you want to achieve in the end, but this looks like a job for "Dependency Injection" - here is a nice sample using autofac

C# Generic T Class TypeOf, is this possible?

I have a class that is generic. Class<T> and depending in the switch statement in the calling code it can be class<int> class<string> class<decimal>
The the method that returns this returns it as an object because the calling code has no idea what it is until after it is set.
Is there a way to do this once I get the object back from the function?
load(object result)
{
Type t = result.GetType().GetGenericArguments()[0];
Class<t> x = (Class<t>) result;
}
Or do I have to set up an a check to check for each type it can be. If int then Class<int>, etc...
EDIT:
Here is what I am trying to do, actual code:
public class ReportResult<TP>
{
public ReportResult()
{
ReportHeaders = new List<ReportHeader>();
ReportViews = new List<IDataAttributeChild<TP>>();
}
public List<ReportHeader> ReportHeaders {get;set;}
public List<IDataAttributeChild<TP>> ReportViews {get;set;}
}
BAL
public object GetReportData(ReportProcedureNameEventArg procedureNameEventArg)
{
object result = null;
switch (procedureNameEventArg.SelectedNode.Class)
{
case ReportClass.Count:
var r = new ReportResult<int>
{
ReportViews = GetCountByReport(procedureNameEventArg),
ReportHeaders = GetReportHeaders(procedureNameEventArg.SelectedNode.ReportViewId)
};
result = r;
break;
case ReportClass.List:
break;
case ReportClass.Date:
var r = new ReportResult<datetime>
{
ReportViews = GetDateSummaryReport(procedureNameEventArg),
ReportHeaders = GetReportHeaders(procedureNameEventArg.SelectedNode.ReportViewId)
};
result = r;
break;
default:
throw new ArgumentOutOfRangeException();
}
return result;
}
The GUI
public void LoadTreeResult(object result)
{
Type t = result.GetType().GetGenericArguments()[0];
ReportResult<?> fff = (ReportResult<?>)result;
dgResult.Columns.Clear();
foreach (var header in result.ReportHeaders)
{
dgResult.Columns.Add(
new DataGridTextColumn
{
Header = header.Header,
Binding = new Binding(header.Binding)
});
}
// This would also be a switch depending on a property coming
// back to now what class to cast to in order to populate the grid.
List<ReportCountByView> d = new List<ReportCountByView>();
foreach (var reportCountByView in result.ReportViews)
{
d.Add((ReportCountByView)reportCountByView);
}
dgResult.ItemsSource = d;
}
This is a layout of the class model in case it might help.
image of layout
Thanks.
if you are going to call the same operation on the instance 'x' after you resolve it you may think about using an interface, this will allow you to define the methods the object performs (and properties) without defining it's type.
your code may then end up looking something like this
public interface IMyInterface
{
void PerformOperation();
}
public class MyGeneric<T> : IMyInterface
{
T Value {get;set;}
MyGeneric(T val)
{
Value = val;
}
void PerformOperation
{
Console.WriteLine("T is {0}", typeof(T));
Console.WriteLine("Value is {0}", Value);
}
}
public void main(string[] args)
{
IMyInterface inst = null;
switch (args[0])
{
case "string":
inst = new MyGeneric("hello");
break;
case "int":
inst = new MyGeneric(7);
break;
case "decimal"
inst = new MyGeneric(18.9M);
break;
}
inst.PerformOperation();
}
What do you want to do with x later? Also, if you can architect it such that x will always be an instance of some baseclass, you could just cast it to the base class.
It depends on how you intend to use the generic types later on. string, int and decimal have no common base type other than object, but they do share some interfaces, so you could possibly choose something like Class<IComparable> if you want to sort them later on. Or you could declare generic constraints if you want to use multiple interfaces:
Class<T> where T : IEquatable<T>, IComparable<T>, IFormattable // and so on...
But what it seems like you're asking for doesn't seem possible. The entire premise of generics is to set the type at design time.
My Opinion
If the result is always being used to invoke same members of the class(es), then can make an interface and implement it to the different classes and get the result as the interface.
This way you will be able to used different classes yet some same members of the interface that are implemented in those classes
I think the best way to do this switch would be to actually implement seperate methods for your different types, and switch at the UI layer.
For example
public ReportResult<int> GetReportDataCount(ReportProcedureNameEventArg procedureNameEventArg)
public ReportResult<DateTime> GetReportDataDate(ReportProcedureNameEventArg procedureNameEventArg)
public void GetReportDataList(ReportProcedureNameEventArg procedureNameEventArg)
The "List" one is the one that really throws me off, because it returns null, otherwise I'd say do something like
public ReportResult<T> GetReportData<T>(ReportProcedureNameEventArg procedureNameEventArg) where T:struct (and then enforce the types programatically).
If you want to try it with dynamics, I think you could make it work that way basically by doing duck typing with the ReportResult, but It might get messy with your List mode in the mix too.

Enum value to string

Does anyone know how to get enum values to string?
example:
private static void PullReviews(string action, HttpContext context)
{
switch (action)
{
case ProductReviewType.Good.ToString():
PullGoodReviews(context);
break;
case ProductReviewType.Bad.ToString():
PullBadReviews(context);
break;
}
}
Edit:
When trying to use ToString(); the compiler complains because the case statement is expecting a constant. I also know that ToString() is is striked out with a line in intellisense
Yes, you can use .ToString() to get a string value for an enum, however you can't use .ToString() in a switch statement. Switch statements need constant expressions, and .ToString() does not evaluate until runtime, so the compiler will throw an error.
To get the behavior you want, with a little change in the approach, you can use enum.Parse() to convert the action string to an enum value, and switch on that enum value instead. As of .NET 4 you can use Enum.TryParse() and do the error checking and handling upfront, rather than in the switch body.
If it were me, I'd parse the string to an enum value and switch on that, rather than switching on the string.
private static void PullReviews(string action, HttpContext context)
{
ProductReviewType review;
//there is an optional boolean flag to specify ignore case
if(!Enum.TryParse(action,out review))
{
//throw bad enum parse
}
switch (review)
{
case ProductReviewType.Good:
PullGoodReviews(context);
break;
case ProductReviewType.Bad:
PullBadReviews(context);
break;
default:
//throw unhandled enum type
}
}
You're going about this backwards. Don't try to use dynamic strings as case labels (you can't), instead parse the string into an enum value:
private static void PullReviews(string action, HttpContext context)
{
// Enum.Parse() may throw if input is invalid, consider TryParse() in .NET 4
ProductReviewType actionType =
(ProductReviewType)Enum.Parse(typeof(ProductReviewType), action);
switch (actionType)
{
case ProductReviewType.Good:
PullGoodReviews(context);
break;
case ProductReviewType.Bad:
PullBadReviews(context);
break;
default: // consider a default case for other possible values...
throw new ArgumentException("action");
}
}
EDIT: In principle, you could just compare to hard-coded strings in your switch statements (see below), but this is the least advisable approach, since it will simply break when you change the values passed in to the method, or the definition of the enum. I'm adding this since it's worth knowing that strings can be used as case labels, so long as they are compile-time literals. Dynamic values can't be used as cases, since the compiler doesn't know about them.
// DON'T DO THIS...PLEASE, FOR YOUR OWN SAKE...
switch (action)
{
case "Good":
PullGoodReviews(context);
break;
case "Bad":
PullBadReviews(context);
break;
}
public enum Color
{
Red
}
var color = Color.Red;
var colorName = Enum.GetName(color.GetType(), color); // Red
Edit: Or perhaps you want..
Enum.Parse(typeof(Color), "Red", true /*ignorecase*/); // Color.Red
There is no TryParse for Enum, so if you expect errors, you have to use try/catch:
try
{
Enum.Parse(typeof(Color), "Red", true /*ignorecase*/);
}
catch( ArgumentException )
{
// no enum found
}
This code is gonna works.
private enum ProductReviewType{good, bad};
private static void PullReviews(string action)
{
string goodAction = Enum.GetName(typeof(ProductReviewType), ProductReviewType.good);
string badAction = Enum.GetName(typeof(ProductReviewType), ProductReviewType.bad);
if (action == goodAction)
{
PullGoodReviews();
}
else if (action == badAction)
{
PullBadReviews();
}
}
public static void PullGoodReviews()
{
Console.WriteLine("GOOD Review!");
}
public static void PullBadReviews()
{
Console.WriteLine("BAD Review...");
}
Since the parsed string is not constant it can not be used by Switch statement.
Compiled in VS2005
You can use another temp variable to save the Type of enum class, this may improve performance.
I'd suggest you go the other way - try to use the actual enum values as much as possible (by converting the string to an enum value as soon as you can), rather than passing strings around your application:
ProductReviewType actionType = (ProductReviewType)Enum.Parse(typeof(ProductReviewType), val);
// You might want to add some error handling here.
PullReviews(actionType);
private static void PullReviews(ProductReviewType action, HttpContext context)
{
switch (action)
{
case ProductReviewType.Good:
PullGoodReviews(context);
break;
case ProductReviewType.Bad:
PullBadReviews(context);
break;
}
}
Note that I've changed your method signature to accept a ProductReviewType argument; this makes it clear what your method actually needs to implement its logic, and fits with my original point that you should try as much as possible not to pass strings around.
This is just for the fun of it, but what if you used a dictionary of delegates? I realize that you have a completely different approach, but dictionary-izing might actually work better for what you're trying to accomplish, especially since it provides a high degree of modularity, as opposed to a mondo-switch construct (although I have to admit, your enum has only 2 members so its a moot issue). Anyways, I just wanted to air a different way to do things...
The dictionary-based approach would kind of look like this:
namespace ConsoleApplication1
{
public enum ProductReviewType
{
Good,
Bad
}
public static class StringToEnumHelper
{
public static ProductReviewType ToProductReviewType(this string target)
{
// just let the framework throw an exception if the parse doesn't work
return (ProductReviewType)Enum.Parse(
typeof(ProductReviewType),
target);
}
}
class Program
{
delegate void ReviewHandler(HttpContext context);
static readonly Dictionary<ProductReviewType, ReviewHandler>
pullReviewOperations =
new Dictionary<ProductReviewType, ReviewHandler>()
{
{ProductReviewType.Good, new ReviewHandler(PullGoodReviews)},
{ProductReviewType.Bad, new ReviewHandler(PullBadReviews)}
};
private static void PullGoodReviews(HttpContext context)
{
// actual logic goes here...
Console.WriteLine("Good");
}
private static void PullBadReviews(HttpContext context)
{
// actual logic goes here...
Console.WriteLine("Bad");
}
private static void PullReviews(string action, HttpContext context)
{
pullReviewOperations[action.ToProductReviewType()](context);
}
static void Main(string[] args)
{
string s = "Good";
pullReviewOperations[s.ToProductReviewType()](null);
s = "Bad";
pullReviewOperations[s.ToProductReviewType()](null);
// pause program execution to review results...
Console.WriteLine("Press enter to exit");
Console.ReadLine();
}
}
}

C# Generics and Type Checking

I have a method that uses an IList<T> as a parameter. I need to check what the type of that T object is and do something based on it. I was trying to use the T value, but the compiler does not not allow it. My solution is the following:
private static string BuildClause<T>(IList<T> clause)
{
if (clause.Count > 0)
{
if (clause[0] is int || clause[0] is decimal)
{
//do something
}
else if (clause[0] is String)
{
//do something else
}
else if (...) //etc for all the types
else
{
throw new ApplicationException("Invalid type");
}
}
}
There has to be a better way to do this. Is there some way I can check the type of T that is passed in and then use a switch statement?
You could use overloads:
public static string BuildClause(List<string> l){...}
public static string BuildClause(List<int> l){...}
public static string BuildClause<T>(List<T> l){...}
Or you could inspect the type of the generic parameter:
Type listType = typeof(T);
if(listType == typeof(int)){...}
You can use typeof(T).
private static string BuildClause<T>(IList<T> clause)
{
Type itemType = typeof(T);
if(itemType == typeof(int) || itemType == typeof(decimal))
...
}
And, because C# has evolved, you can (now) use pattern matching.
private static string BuildClause<T>(IList<T> clause)
{
if (clause.Count > 0)
{
switch (clause[0])
{
case int x: // do something with x, which is an int here...
case decimal x: // do something with x, which is a decimal here...
case string x: // do something with x, which is a string here...
...
default: throw new Exception("Invalid type");
}
}
}
And again with switch expressions in C# 8.0, the syntax gets even more succinct.
private static string BuildClause<T>(IList<T> clause)
{
if (clause.Count > 0)
{
return clause[0] switch
{
int x => "some string related to this int",
decimal x => "some string related to this decimal",
string x => x,
...,
_ => throw new Exception("Invalid type")
}
}
}
I hope you find this helpful:
typeof(IList<T>).IsGenericType == true
typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
typeof(IList<int>).GetGenericArguments()[0] == typeof(int)
https://dotnetfiddle.net/5qUZnt
By default know there is not a great way. Awhile back I got frustrated with this and wrote a little utility class that helped out a bit and made the syntax a bit cleaner. Essentially it turns the code into
TypeSwitcher.Do(clause[0],
TypeSwitch.Case<int>(x => ...), // x is an int
TypeSwitch.Case<decimal>(d => ...), // d is a decimal
TypeSwitch.Case<string>(s => ...)); // s is a string
Full blog post and details on the implementation are available here
http://blogs.msdn.com/jaredpar/archive/2008/05/16/switching-on-types.aspx
The typeof operator...
typeof(T)
... won't work with the c# switch statement. But how about this? The following post contains a static class...
Is there a better alternative than this to 'switch on type'?
...that will let you write code like this:
TypeSwitch.Do(
sender,
TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));
There is no way to use the switch statement for what you want it to do. The switch statement must be supplied with integral types, which does not include complex types such as a "Type" object, or any other object type for that matter.
For everyone that says checking types and doing something based on the type is not a great idea for generics I sort of agree but I think there could be some circumstances where this perfectly makes sense.
For example if you have a class that say is implemented like so (Note: I am not showing everything that this code does for simplicity and have simply cut and pasted into here so it may not build or work as intended like the entire code does but it gets the point across. Also, Unit is an enum):
public class FoodCount<TValue> : BaseFoodCount
{
public TValue Value { get; set; }
public override string ToString()
{
if (Value is decimal)
{
// Code not cleaned up yet
// Some code and values defined in base class
mstrValue = Value.ToString();
decimal mdecValue;
decimal.TryParse(mstrValue, out mdecValue);
mstrValue = decimal.Round(mdecValue).ToString();
mstrValue = mstrValue + mstrUnitOfMeasurement;
return mstrValue;
}
else
{
// Simply return a string
string str = Value.ToString() + mstrUnitOfMeasurement;
return str;
}
}
}
...
public class SaturatedFat : FoodCountWithDailyValue<decimal>
{
public SaturatedFat()
{
mUnit = Unit.g;
}
}
public class Fiber : FoodCount<int>
{
public Fiber()
{
mUnit = Unit.g;
}
}
public void DoSomething()
{
nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat();
string mstrValueToDisplayPreFormatted= oSatFat.ToString();
}
So in summary, I think there are valid reasons why you might want to check to see what type the generic is, in order to do something special.
Your construction completely defeats the purpose of a generic method. It's ugly on purpose because there must be a better way to achieve what you're trying to accomplish, although you haven't given us quite enough information to figure out what that is.
You can do typeOf(T), but I would double check your method and make sure your not violating single responsability here. This would be a code smell, and that's not to say it shouldn't be done but that you should be cautious.
The point of generics is being able to build type-agnostic algorthims were you don't care what the type is or as long as it fits within a certain set of criteria. Your implementation isn't very generic.
How about this :
// Checks to see if the value passed is valid.
if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value))
{
throw new ArgumentException();
}
My two cents:
In case you happen to have a generic method that returns a generic value but doesn't have generic parameters, you can use default(T) + (T)(object) cast, together with C# 8 pattern matching/type checks (as indicated in the other recent answers).
Example:
private static T Parse<T>(string str)
{
return default(T) switch
{
short => (T)(object)short.Parse(str),
ushort => (T)(object)ushort.Parse(str),
int => (T)(object)int.Parse(str),
uint => (T)(object)uint.Parse(str),
long => (T)(object)long.Parse(str),
ulong => (T)(object)ulong.Parse(str),
_ => throw new ArgumentException()
};
}

Using Case/Switch and GetType to determine the object [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
C# - Is there a better alternative than this to ‘switch on type’?
If you want to switch on a type of object, what is the best way to do this?
Code snippet
private int GetNodeType(NodeDTO node)
{
switch (node.GetType())
{
case typeof(CasusNodeDTO):
return 1;
case typeof(BucketNodeDTO):
return 3;
case typeof(BranchNodeDTO):
return 0;
case typeof(LeafNodeDTO):
return 2;
default:
return -1;
}
}
I know this doesn't work that way, but I was wondering how you could solve this.
Is an if/else statement appropriate in this case?
Or do you use the switch and add .ToString() to the type?
This won't directly solve your problem as you want to switch on your own user-defined types, but for the benefit of others who only want to switch on built-in types, you can use the TypeCode enumeration:
switch (Type.GetTypeCode(node.GetType()))
{
case TypeCode.Decimal:
// Handle Decimal
break;
case TypeCode.Int32:
// Handle Int32
break;
...
}
If I really had to switch on type of object, I'd use .ToString(). However, I would avoid it at all costs: IDictionary<Type, int> will do much better, visitor might be an overkill but otherwise it is still a perfectly fine solution.
In the MSDN blog post Many Questions: switch on type is some information on why .NET does not provide switching on types.
As usual - workarounds always exists.
This one isn't mine, but unfortunately I have lost the source. It makes switching on types possible, but I personally think it's quite awkward (the dictionary idea is better):
public class Switch
{
public Switch(Object o)
{
Object = o;
}
public Object Object { get; private set; }
}
/// <summary>
/// Extensions, because otherwise casing fails on Switch==null
/// </summary>
public static class SwitchExtensions
{
public static Switch Case<T>(this Switch s, Action<T> a)
where T : class
{
return Case(s, o => true, a, false);
}
public static Switch Case<T>(this Switch s, Action<T> a,
bool fallThrough) where T : class
{
return Case(s, o => true, a, fallThrough);
}
public static Switch Case<T>(this Switch s,
Func<T, bool> c, Action<T> a) where T : class
{
return Case(s, c, a, false);
}
public static Switch Case<T>(this Switch s,
Func<T, bool> c, Action<T> a, bool fallThrough) where T : class
{
if (s == null)
{
return null;
}
T t = s.Object as T;
if (t != null)
{
if (c(t))
{
a(t);
return fallThrough ? s : null;
}
}
return s;
}
}
Usage:
new Switch(foo)
.Case<Fizz>
(action => { doingSomething = FirstMethodCall(); })
.Case<Buzz>
(action => { return false; })
I'm faced with the same problem and came across this post.
Is this what's meant by the IDictionary approach:
Dictionary<Type, int> typeDict = new Dictionary<Type, int>
{
{typeof(int),0},
{typeof(string),1},
{typeof(MyClass),2}
};
void Foo(object o)
{
switch (typeDict[o.GetType()])
{
case 0:
Print("I'm a number.");
break;
case 1:
Print("I'm a text.");
break;
case 2:
Print("I'm classy.");
break;
default:
break;
}
}
If so, I can't say I'm a fan of reconciling the numbers in the dictionary with the case statements.
This would be ideal but the dictionary reference kills it:
void FantasyFoo(object o)
{
switch (typeDict[o.GetType()])
{
case typeDict[typeof(int)]:
Print("I'm a number.");
break;
case typeDict[typeof(string)]:
Print("I'm a text.");
break;
case typeDict[typeof(MyClass)]:
Print("I'm classy.");
break;
default:
break;
}
}
Is there another implementation I've overlooked?
I'd just use an if statement. In this case:
Type nodeType = node.GetType();
if (nodeType == typeof(CasusNodeDTO))
{
}
else ...
The other way to do this is:
if (node is CasusNodeDTO)
{
}
else ...
The first example is true for exact types only, where the latter checks for inheritance too.
You can do this:
function void PrintType(Type t) {
var t = true;
new Dictionary<Type, Action>{
{typeof(bool), () => Console.WriteLine("bool")},
{typeof(int), () => Console.WriteLine("int")}
}[t.GetType()]();
}
It's clear and its easy.
It a bit slower than caching the dictionary somewhere.. but for lots of code this won't matter anyway..
You can do this:
if (node is CasusNodeDTO)
{
...
}
else if (node is BucketNodeDTO)
{
...
}
...
While that would be more elegant, it's possibly not as efficient as some of the other answers here.
One approach is to add a pure virtual GetNodeType() method to NodeDTO and override it in the descendants so that each descendant returns actual type.
Depending on what you are doing in the switch statement, the correct answer is polymorphism. Just put a virtual function in the interface/base class and override for each node type.
I actually prefer the approach given as the answer here:
Is there a better alternative than this to 'switch on type'?
There is however a good argument about not implementing any type comparison methids in an object oriented language like C#. You could as an alternative extend and add extra required functionality using inheritance.
This point was discussed in the comments of the authors blog here:
http://blogs.msdn.com/b/jaredpar/archive/2008/05/16/switching-on-types.aspx#8553535
I found this an extremely interesting point which changed my approach in a similar situation and only hope this helps others.
Kind Regards, Wayne

Categories