C# - Which Is the Best Alternative to 'Switch on Type'

Is there a better alternative than this to 'switch on type'?

Switching on types is definitely lacking in C# (UPDATE: in C#7 / VS 2017 switching on types is supported - see Zachary Yates's answer). In order to do this without a large if/else if/else statement, you'll need to work with a different structure. I wrote a blog post awhile back detailing how to build a TypeSwitch structure.

https://learn.microsoft.com/archive/blogs/jaredpar/switching-on-types

Short version: TypeSwitch is designed to prevent redundant casting and give a syntax that is similar to a normal switch/case statement. For example, here is TypeSwitch in action on a standard Windows form event

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"));

The code for TypeSwitch is actually pretty small and can easily be put into your project.

static class TypeSwitch {
public class CaseInfo {
public bool IsDefault { get; set; }
public Type Target { get; set; }
public Action<object> Action { get; set; }
}

public static void Do(object source, params CaseInfo[] cases) {
var type = source.GetType();
foreach (var entry in cases) {
if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
entry.Action(source);
break;
}
}
}

public static CaseInfo Case<T>(Action action) {
return new CaseInfo() {
Action = x => action(),
Target = typeof(T)
};
}

public static CaseInfo Case<T>(Action<T> action) {
return new CaseInfo() {
Action = (x) => action((T)x),
Target = typeof(T)
};
}

public static CaseInfo Default(Action action) {
return new CaseInfo() {
Action = x => action(),
IsDefault = true
};
}
}

Is there a better alternative than this to 'switch on type'?

Switching on types is definitely lacking in C# (UPDATE: in C#7 / VS 2017 switching on types is supported - see Zachary Yates's answer). In order to do this without a large if/else if/else statement, you'll need to work with a different structure. I wrote a blog post awhile back detailing how to build a TypeSwitch structure.

https://learn.microsoft.com/archive/blogs/jaredpar/switching-on-types

Short version: TypeSwitch is designed to prevent redundant casting and give a syntax that is similar to a normal switch/case statement. For example, here is TypeSwitch in action on a standard Windows form event

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"));

The code for TypeSwitch is actually pretty small and can easily be put into your project.

static class TypeSwitch {
public class CaseInfo {
public bool IsDefault { get; set; }
public Type Target { get; set; }
public Action<object> Action { get; set; }
}

public static void Do(object source, params CaseInfo[] cases) {
var type = source.GetType();
foreach (var entry in cases) {
if (entry.IsDefault || entry.Target.IsAssignableFrom(type)) {
entry.Action(source);
break;
}
}
}

public static CaseInfo Case<T>(Action action) {
return new CaseInfo() {
Action = x => action(),
Target = typeof(T)
};
}

public static CaseInfo Case<T>(Action<T> action) {
return new CaseInfo() {
Action = (x) => action((T)x),
Target = typeof(T)
};
}

public static CaseInfo Default(Action action) {
return new CaseInfo() {
Action = x => action(),
IsDefault = true
};
}
}

Alternative for switch statement in c#

Based on the comments under your question, I discovered it is possible to implement what @Jon Skeet stated.

You can add an Initialize Method in your RootObject class to create the dictionary (Use a ref Dictionary so to avoid setting the dictionary in your RootObject class that could change the structure of your serialization):

 public void Initialize(ref Dictionary<string, Func<Rootobject>> rootDic)
{
Func<Rootobject> shouldFunc = () =>
{
Rootobject root = new Rootobject();
root.should = new Should[1];
Should objShould = new Should();
objShould.match = new Match();
objShould.match.pname = "hello";
root.should[0] = objShould;

return root;
};

Func<Rootobject> mustFunc = () =>
{
Rootobject root = new Rootobject();
root.must = new Must[1];
Must objMust = new Must();
objMust.match = new Match();
objMust.match.pname = "hello";
root.must[0] = objMust;

return root;
};
rootDic.Add("should", shouldFunc);
rootDic.Add("must", mustFunc);
}

And then call it in your getobject method like so:

 public static Rootobject getobject(string op)
{
Dictionary<string, Func<Rootobject>> rootDic = new Dictionary<string,Func<Rootobject>>();
Rootobject root = new Rootobject();
root.Initialize(ref rootDic);

if(rootDic.Count > 0)
return rootDic[op].Invoke();

return new Rootobject();
}

You still going to get the same result as the solution in your question even after serializing it.

looking alternative way for this switch case or any other solution

Instead of passing in the parameter MasterSectionEnum, change your method to accept a type parameter. Then just use that generic type in a single call of UpdateRevision.

public async Task<MutationResponse> SetRequestStage<SectionType>(
string requestStage,
Guid requestId
) {

...
await UpdateRevision<SectionType>(request.DataId).ConfigureAwait(false);
....

}

If all valid types of SectionType share an interface or are derived from the same class, then you can take it further and add a constraint to the SectionType and avoid having to handle bad types at runtime.




Edit:

So SetRequestStage can't be generic huh? Well, the inclination to make it generic stems from the fact that it depends on UpdateRevision, which is generic. This in turn depends on DbContext.Set(). And you're using a generic version of it. But the good news is that there seems to be a non-generic version of it that accepts a type variable as a parameter. So:

async Task UpdateRevision<T>(
Guid id,
Type t
) where T : class, IAEIMaster, IRevisionData {

var dbSet = this._dbContext.Set(t);
...

}

And then:

public async Task<MutationResponse> SetRequestStage(
string requestStage,
Guid requestId,
Type SectionType
) {

...
await UpdateRevision(request.DataId, SectionType).ConfigureAwait(false);
....

}

I don't know what your UI looks like. But, generically:

var dic = new Dictionary<MasterSectionEnum, Type> {
{ MasterSectionEnum.LOCALCODE, typeof(LocalCode) },
{ MasterSectionEnum.NATIONALCODE, typeof(NationalCode) },
...
};

public async someUiRelatedMethod(
string reqStage,
Guid reqId,
MasterSectionEnum sectionType
) {

await SetRequestStage(reqStage, reqId, dic[sectionType]);

}

Sorry if the syntax on the latter isn't quite right. But you get the idea.

What is a good substitute for a big switch-case?

You can store country-power pairs into a Dictionary<string, int> then just get the score of a particular country by using indexer:

var points = new Dictionary<string,int>();
// populate the dictionary...
var usa = points["USA"];

Edit: As suggested in comments you should store the information in external file, for example an xml would be a good choice. That way you don't have to modify the code to add or remove countries. You just need to store them into XML file, edit it whenever you need.Then parse it when your program starts, and load the values into Dictionary.You can use LINQ to XML for that.If you haven't use it before there are good examples in the documentation to get you started.

C# switch on type

See another answer; this feature now exists in C#


I usually use a dictionary of types and delegates.

var @switch = new Dictionary<Type, Action> {
{ typeof(Type1), () => ... },
{ typeof(Type2), () => ... },
{ typeof(Type3), () => ... },
};

@switch[typeof(MyType)]();

It's a little less flexible as you can't fall through cases, continue etc. But I rarely do so anyway.

Alternative to switch statement to call methods in c#

This might be overkill for you but you could do something like this. I learned this approach when researching the O part of S.O.L.I.D (design principles). The nice thing about this method is that when a new report is required you just create a class that derives from BaseReportGenerator for it. I've demonstrated the method using assembly scanning to get the relevant classes but another method would be to have them all injected as dependencies.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace AltToSwitch
{
class Program
{
static void Main(string[] args)
{
var myReportType = "A";
var model = "myModel";
var language = "myLanguage";

var reportGenerators = new List<BaseReportGenerator>();

//use assembly scanning to get all report generator implementations
foreach (Type type in
Assembly.GetAssembly(typeof(BaseReportGenerator)).GetTypes()
.Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(BaseReportGenerator))))
{
reportGenerators.Add((BaseReportGenerator)Activator.CreateInstance(type));
}

var reportGeneratorToUse = reportGenerators.SingleOrDefault(x => x.ReportName == myReportType);
Console.WriteLine(reportGeneratorToUse.GenerateReport(model, language));
}
}

public abstract class BaseReportGenerator
{
public abstract string ReportName
{
get;
}
public abstract string GenerateReport(string model, string language);

//common report functionality could go here
}
public class TheAReportGenerator : BaseReportGenerator
{
public override string ReportName => "A";

public override string GenerateReport(string model, string language)
{
return "The A report";
}
}
}


Related Topics



Leave a reply



Submit