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();
}
}
}
Related
I have seen suggestions for both but would like to confirm that this is the best way to do this:
public enum MO
{
Learn = 0,
Practice = 1,
Quiz = 2
}
public static partial class Extensions
{
public static MO ToMode(this string mode)
{
switch (mode)
{
case "Learn": return MO.Learn;
case "Practice": return MO.Practice;
case "Quiz": return MO.Quiz;
default: throw new InvalidEnumArgumentException("Unhandled value: " + mode.ToString());
}
}
}
I would use ArgumentException in this case. The documentation for InvalidEnumArgumentException states:
This exception is thrown if you pass an invalid enumeration value to a method or when setting a property.
which is not what you're doing here. You're passing a string, not an enumeration. The description for ArgumentException looks more appropriate:
The exception that is thrown when one of the arguments provided to a method is not valid.
You shouldn't throw at all. Stay pure.
public static (bool, MO) ToMode(this string mode)
{
switch (mode)
{
case "Learn": return (true, MO.Learn);
case "Practice": return (true, MO.Practice);
case "Quiz": return (true, MO.Quiz);
default: return (false, default);
}
}
...let your caller to check the flag and decide what to do.
Other way to do that is to have MO.Unset option serving as default "not recognized" label. Then you simply return your MO.Unset whenever input argument is unexpected.
Keep your code pure, young jedi.
P.S. It is always good practice to have a default value on the enum even if you won't use it in a way I've recommended: enum's are value type and thus always have non-null default value; in case you won't provide one, it is quite possible to mess up.
I'd be inclined to let the framework decide. Here's the way the framework does it:
public static partial class Extensions
{
public static MO ToMode(this string mode) => (MO)Enum.Parse(typeof(MO), mode);
}
Now that throws an ArgumentException if you pass an invalid string.
Alternatively, you could implement TryParseMode to avoid exceptions entirely:
public static partial class Extensions
{
private static Dictionary<string, MO> _lookup =
Enum
.GetNames(typeof(MO))
.ToDictionary(n => n, n => n.ToMode());
public static bool TryParseMode(this string mode, out MO value)
{
var found = _lookup.ContainsKey(mode);
value = found ? _lookup[mode] : default(MO);
return found;
}
public static MO ToMode(this string mode) => (MO)Enum.Parse(typeof(MO), mode);
}
You'd use that like this:
Console.WriteLine("Learn".TryParseMode(out MO m1));
Console.WriteLine("LearnX".TryParseMode(out MO m2));
...which returns:
True
False
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 ;)
I have the following code:
public void ParseNetworkPacket(IAsyncResult iResult)
{
NetworkConnection networkConnection = (NetworkConnection)iResult.AsyncState;
string teste = NetworkPacketType.ToString();
switch (this.NetworkPacketType)
{
case NetworkPacketType.ShotPacket:
break;
case NetworkPacketType.ShotResponsePacket:
break;
case NetworkPacketType.ChatMessagePacket:
break;
default:
break;
}
networkConnection.BeginReadPacket();
}
NetworkPacketType is a enum defined by me.
In the switch, depending on the type of the enum, I would call a different method.
I would like to do that not using switch, because I may have too many enum types.
Is there any other way to do that? Or wih an enum that's the only possible way?
Beside using a map as suggested in the answer linked by Veer, you could also use reflection. If you would name your methods like the enum values for example, it could be done like this:
public void ParseNetworkPacket(IAsyncResult iResult)
{
NetworkConnection networkConnection = (NetworkConnection)iResult.AsyncState;
string teste = NetworkPacketType.ToString();
string methodName = this.NetworkPacketType.ToString();
MethodInfo methodInfo = GetType().GetMethod(methodName,
BindingFlags.Instance | BindingFlags.NonPublic);
methodInfo.Invoke(this, /* your arguments here */);
networkConnection.BeginReadPacket();
}
private void ShotPacket()
{
....
}
But I would not really recommend this approach if not absolutely neccessary. It can be a pain to maintain this, among other things.
I currently have a switch statement that runs around 300 odd lines. I know this is not as giant as it can get, but I'm sure there's a better way to handle this.
The switch statement takes an Enum that is used to determine certain properties that pertain to logging. Right now the problem sets in that it is very easy to leave out an enumeration value and that it will not be given a value as it is not in the switch statement.
Is there an option one can use to ensure that every enumeration is used and given a custom set of values it needs to do its job?
EDIT:
Code sample as requested: (This is simplistic, but shows exactly what I mean. Also an Enumeration would exist with the below values.)
internal void GenerateStatusLog(LogAction ActionToLog)
{
switch (ActionToLog)
{
case LogAction.None:
{
return;
}
case LogAction.LogThis:
{
ActionText = "Logging this Information";
LogText = "Go for it.";
break;
}
}
// .. Do everything else
}
EDIT
I thought this over again, looked around in related questions in SO, and I wrote some code. I created a class named AdvancedSwitch<T>, which allows you to add cases and exposes a method to evaluate a value and lets you specify values that it should check for existence.
This is what I came up with:
public class AdvancedSwitch<T> where T : struct
{
protected Dictionary<T, Action> handlers = new Dictionary<T, Action>();
public void AddHandler(T caseValue, Action action)
{
handlers.Add(caseValue, action);
}
public void RemoveHandler(T caseValue)
{
handlers.Remove(caseValue);
}
public void ExecuteHandler(T actualValue)
{
ExecuteHandler(actualValue, Enumerable.Empty<T>());
}
public void ExecuteHandler(T actualValue, IEnumerable<T> ensureExistence)
{
foreach (var val in ensureExistence)
if (!handlers.ContainsKey(val))
throw new InvalidOperationException("The case " + val.ToString() + " is not handled.");
handlers[actualValue]();
}
}
You can consume the class this way:
public enum TrafficColor { Red, Yellow, Green }
public static void Main()
{
Console.WriteLine("Choose a traffic color: red, yellow, green?");
var color = (TrafficColor)Enum.Parse(typeof(TrafficColor), Console.ReadLine());
var result = string.Empty;
// Creating the "switch"
var mySwitch = new AdvancedSwitch<TrafficColor>();
// Adding a single case
mySwitch.AddHandler(TrafficColor.Green, delegate
{
result = "You may pass.";
});
// Adding multiple cases with the same action
Action redAndYellowDelegate = delegate
{
result = "You may not pass.";
};
mySwitch.AddHandler(TrafficColor.Red, redAndYellowDelegate);
mySwitch.AddHandler(TrafficColor.Yellow, redAndYellowDelegate);
// Evaluating it
mySwitch.ExecuteHandler(color, (TrafficColor[])Enum.GetValues(typeof(TrafficColor)));
Console.WriteLine(result);
}
With the creative use of anonymous delegates, you can easily add new cases to your "switch block". :)
Not that you can also use lambda expressions, and lambda blocks, eg () => { ... } instead of delegate { ... }.
You can easily use this class instead of the long switch blocks.
Original post:
If you use Visual Studio, always create swich statements with the switch code snippet. Type switch press tab twice, and it auto-generates all the possibilities for you.
Then, add a default case to the end which throws an exception, that way when testing your app you will notice that there is an unhandled case, instantly.
I mean something like this:
switch (something)
{
...
case YourEnum.SomeValue:
...
break;
default:
throw new InvalidOperationException("Default case reached.");
}
Well, there's throwing in the default case... There's no edit / compile time construct other than that.
However Strategy, Visitor and other patterns related to them may be appropriate if you choose to do it at run time.
Sample code will help with getting the best answer.
EDIT: Thanks for the sample. I still think it needs a bit of fleshing out as you dont cover whether there are some parameters that only apply to some cases etc.
Action is often used as an alias for the Command pattern and the fact that your Enum is called LogAction signifies that each value carries with it a behavior - be that implied (you stick appropriate code in a case) or explicit (in the specific Command hierarchy class).
Thus it looks to me like a usage of the Command pattern is appropriate (though your sample doesnt prove it) - i.e., have a class (potentially a hierarchy using constructor overloads or any other [set of] factory mechanisms) that keeps the state associated with the request along with the specific behaviour. Then, instead of passing an Enum value, create an appropriate LogCommand instance to the logger, which just invokes it (potentially passing a Log Sink 'receptacle' which the Command can log into). Otherwise you're poking random subsets of parameters in different places.
SEEALSO related posts:
C# - Is there a better alternative than this to ‘switch on type’?
Replace giant switch statement with what?
One possible solution is to use a SortedDictionary:
delegate void EnumHandler (args);
SortedDictionary <Enum, EnumHandler> handlers;
constructor
{
handlers = new SortedDictionary <Enum, EnumHandler> ();
fill in handlers
}
void SomeFunction (Enum enum)
{
EnumHandler handler;
if (handlers.TryGetValue (enum, out handler))
{
handler (args);
}
else
{
// not handled, report an error
}
}
This method does allow you to replace the handlers dynamically. You could also use a List as the value part of the dictionary and have multiple handlers for each enum.
Try to use reflection.
Decorate enum options with attributes that holds associated value and return this value.
Create static class of constants and use reflection for mapping enum-option to constant by name
hope this will help
Some times storing the options in a map is a good solution, you can externalize the configuration to a file too, not sure if it applies to your application.
Long code example here, and the final generic code is a little heavy (EDIT have added an extra example that eliminates the need for the angle brackets at the expense of some final flexibility).
One thing that this solution will give you is good performance - not quite as good as a straightforward switch statement, but each case statement becomes a dictionary lookup and method invocation, so still pretty good. The first call will get a performance penalty, however, due to the use of a static generic that reflects on initialisation.
Create an attribute and generic type as follows:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class DynamicSwitchAttribute : Attribute
{
public DynamicSwitchAttribute(Type enumType, params object[] targets)
{ Targets = new HashSet<object>(targets); EnumType = enumType; }
public Type EnumType { get; private set; }
public HashSet<object> Targets { get; private set; }
}
//this builds a cache of methods for a given TTarget type, with a
//signature equal to TAction,
//keyed by values of the type TEnum. All methods are expected to
//be instance methods.
//this code can easily be modified to support static methods instead.
//what would be nice here is if we could enforce a generic constraint
//on TAction : Delegate, but we can't.
public static class DynamicSwitch<TTarget, TEnum, TAction>
{
//our lookup of actions against enum values.
//note: no lock is required on this as it is built when the static
//class is initialised.
private static Dictionary<TEnum, TAction> _actions =
new Dictionary<TEnum, TAction>();
private static MethodInfo _tActionMethod;
private static MethodInfo TActionMethod
{
get
{
if (_tActionMethod == null)
{
//one criticism of this approach might be that validation exceptions
//will be thrown inside a TypeInitializationException.
_tActionMethod = typeof(TAction).GetMethod("Invoke",
BindingFlags.Instance | BindingFlags.Public);
if (_tActionMethod == null)
throw new ArgumentException(/*elided*/);
//verify that the first parameter type is compatible with our
//TTarget type.
var methodParams = _tActionMethod.GetParameters();
if (methodParams.Length == 0)
throw new ArgumentException(/*elided*/);
//now check that the first parameter is compatible with our type TTarget
if (!methodParams[0].ParameterType.IsAssignableFrom(typeof(TTarget)))
throw new ArgumentException(/*elided*/);
}
return _tActionMethod;
}
}
static DynamicSwitch()
{
//examine the type TTarget to extract all public instance methods
//(you can change this to private instance if need be) which have a
//DynamicSwitchAttribute defined.
//we then project the attributes and the method into an anonymous type
var possibleMatchingMethods =
from method in typeof(TTarget).
GetMethods(BindingFlags.Public | BindingFlags.Instance)
let attributes = method.GetCustomAttributes(
typeof(DynamicSwitchAttribute), true).
Cast<DynamicSwitchAttribute>().ToArray()
where attributes!= null && attributes.Length == 1
&& attributes[0].EnumType.Equals(typeof(TEnum))
select new { Method = method, Attribute = attributes[0] };
//create linq expression parameter expressions for each of the
//delegate type's parameters
//these can be re-used for each of the dynamic methods we generate.
ParameterExpression[] paramExprs = TActionMethod.GetParameters().
Select((pinfo, index) =>
Expression.Parameter(
pinfo.ParameterType, pinfo.Name ?? string.Format("arg{0}"))
).ToArray();
//pre-build an array of these parameter expressions that only
//include the actual parameters
//for the method, and not the 'this' parameter.
ParameterExpression[] realParamExprs = paramExprs.Skip(1).ToArray();
//this has to be generated for each target method.
MethodCallExpression methodCall = null;
foreach (var match in possibleMatchingMethods)
{
if (!MethodMatchesAction(match.Method))
continue;
//right, now we're going to use System.Linq.Expressions to build
//a dynamic expression to invoke this method given an instance of TTarget.
methodCall =
Expression.Call(
Expression.Convert(
paramExprs[0], typeof(TTarget)
),
match.Method, realParamExprs);
TAction dynamicDelegate = Expression.
Lambda<TAction>(methodCall, paramExprs).Compile();
//now we have our method, we simply inject it into the dictionary, using
//all the unique TEnum values (from the attribute) as the keys
foreach (var enumValue in match.Attribute.Targets.OfType<TEnum>())
{
if (_actions.ContainsKey(enumValue))
throw new InvalidOperationException(/*elided*/);
_actions[enumValue] = dynamicDelegate;
}
}
}
private static bool MethodMatchesAction(MethodInfo method)
{
//so we want to check that the target method matches our desired
//delegate type (TAction).
//The way this is done is to fetch the delegate type's Invoke
//method (implicitly invoked when you invoke delegate(args)), and
//then we check the return type and parameters types of that
//against the return type and args of the method we've been passed.
//if the target method's return type is equal to or derived from the
//expected delegate's return type, then all is good.
if (!_tActionMethod.ReturnType.IsAssignableFrom(method.ReturnType))
return false;
//now, the parameter lists of the method will not be equal in length,
//as our delegate explicitly includes the 'this' parameter, whereas
//instance methods do not.
var methodParams = method.GetParameters();
var delegateParams = TActionMethod.GetParameters();
for (int i = 0; i < methodParams.Length; i++)
{
if (!methodParams[i].ParameterType.IsAssignableFrom(
delegateParams[i + 1].ParameterType))
return false;
}
return true;
}
public static TAction Resolve(TEnum value)
{
TAction result;
if (!_actions.TryGetValue(value, out result))
throw new ArgumentException("The value is not mapped");
return result;
}
}
Now do this in a Unit Test:
[TestMethod]
public void TestMethod1()
{
Assert.AreEqual(1,
DynamicSwitch<UnitTest1, Blah, Func<UnitTest1, int>>.
Resolve(Blah.BlahBlah)(this));
Assert.AreEqual(125,
DynamicSwitch<UnitTest1, Blah, Func<UnitTest1, int>>.
Resolve(Blah.Blip)(this));
Assert.AreEqual(125,
DynamicSwitch<UnitTest1, Blah, Func<UnitTest1, int>>.
Resolve(Blah.Bop)(this));
}
public enum Blah
{
BlahBlah,
Bloo,
Blip,
Bup,
Bop
}
[DynamicSwitchAttribute(typeof(Blah), Blah.BlahBlah)]
public int Method()
{
return 1;
}
[DynamicSwitchAttribute(typeof(Blah), Blah.Blip, Blah.Bop)]
public int Method2()
{
return 125;
}
So, given a value of TEnum, and your preferred 'action' type (in your code you would appear to be simply returning nothing and modifying the internal state of the class), you simply consult the DynamicSwitch<> class, ask it to resolve a target method, and then invoke it inline (passing the target object on which the method will be invoked as the first parameter).
I'm not really expecting any votes for this - it's a MAD solution to be honest (it does have the advantage of being able to be applied for any enum type, and even discreet values of type int/float/double, as well as supporting any delegate type) - so perhaps it's a bit of a sledgehammer!
EDIT
Once you have a static generic like this, angle-bracket hell ensues - so we want to try and get rid of them. A lot of the time, this is done by type inference on method parameters etc - but we have a problem here that we can't easily infer a delegate's signature without repeating the method call i.e. (args) => return.
However, you seem to require a method that takes no parameters and returns void, so you can close over this behemoth generic by fixing the delegate type to Action, and throw a fluid API into the mix as well (if that's your kind of thing):
public static class ActionSwitch
{
public class SwitchOn<TEnum>
{
private TEnum Value { get; set; }
internal SwitchOn(TEnum value)
{
Value = value;
}
public class Call<TTarget>{
private TEnum Value { get; set; }
private TTarget Target { get; set; }
internal Call(TEnum value, TTarget target)
{
Value = value;
Target = target;
Invoke();
}
internal void Invoke(){
DynamicSwitch<TTarget, TEnum, Action<TTarget>>.Resolve(Value)(Target);
}
}
public Call<TTarget> On<TTarget>(TTarget target)
{
return new Call<TTarget>(Value, target);
}
}
public static SwitchOn<TEnum> Switch<TEnum>(TEnum onValue)
{
return new SwitchOn<TEnum>(onValue);
}
}
Now add this to the test project:
[TestMethod]
public void TestMethod2()
{
//no longer have any angle brackets
ActionSwitch.Switch(Blah.Bup).On(this);
Assert.IsTrue(_actionMethod1Called);
}
private bool _actionMethod1Called;
[DynamicSwitch(typeof(Blah), Blah.Bup)]
public void ActionMethod1()
{
_actionMethod1Called = true;
}
Only issue with this (apart from the complexity of the solution :) ) is that you'd have to re-build this static wrapper type whenever you want to use a new type of target delegate for a dynamic switch elsewhere. You could generate a generic version based on the Action<...> and Func<...> delegates that incorporates TArg1, TArg(n) and TReturn (if Func<>) - but you'd end up writing a lot more code.
Perhaps I'll turn this into an article on my blog and do all of that - if I get the time!
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