C # switch
이 유형에 있을 수없는 것으로 보았을 때 ( is
관계가 하나 이상의 구별 case
이 적용될 수 있기 때문에 특별한 경우로 추가되지 않았 음 ), 이것 이외의 유형에 대한 전환을 시뮬레이션하는 더 좋은 방법이 있습니까?
void Foo(object o)
{
if (o is A)
{
((A)o).Hop();
}
else if (o is B)
{
((B)o).Skip();
}
else
{
throw new ArgumentException("Unexpected type: " + o.GetType());
}
}
답변
C #에서는 유형 전환이 확실히 부족합니다 ( 업데이트 : C # 7 / VS 2017에서는 유형 전환이 지원됩니다- 아래 Zachary Yates의 답변 참조 ). 큰 if / else if / else 문없이이 작업을 수행하려면 다른 구조로 작업해야합니다. TypeSwitch 구조를 작성하는 방법을 자세히 설명하는 블로그 게시물을 잠시 동안 작성했습니다.
https://docs.microsoft.com/archive/blogs/jaredpar/switching-on-types
짧은 버전 : TypeSwitch는 중복 캐스팅을 방지하고 일반 switch / case 문과 유사한 구문을 제공하도록 설계되었습니다. 예를 들어 다음은 표준 Windows 양식 이벤트에서 작동중인 TypeSwitch입니다.
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"));
TypeSwitch의 코드는 실제로 매우 작으며 프로젝트에 쉽게 넣을 수 있습니다.
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
};
}
}
답변
Visual Studio 2017 (릴리스 15. *)과 함께 제공되는 C # 7 을 사용하면 case
명령문 에서 패턴 을 사용할 수 있습니다 (패턴 일치).
switch(shape)
{
case Circle c:
WriteLine($"circle with radius {c.Radius}");
break;
case Rectangle s when (s.Length == s.Height):
WriteLine($"{s.Length} x {s.Height} square");
break;
case Rectangle r:
WriteLine($"{r.Length} x {r.Height} rectangle");
break;
default:
WriteLine("<unknown shape>");
break;
case null:
throw new ArgumentNullException(nameof(shape));
}
C # 6에서는 nameof () 연산자 와 함께 switch 문을 사용할 수 있습니다 (@Joey Adams 덕분에).
switch(o.GetType().Name) {
case nameof(AType):
break;
case nameof(BType):
break;
}
C # 5 및 이전 버전에서는 switch 문을 사용할 수 있지만 유형 이름이 포함 된 마술 문자열을 사용해야합니다 … 특별히 리팩터링하지 않습니다 (@nukefusion 덕분에)
switch(o.GetType().Name) {
case "AType":
break;
}
답변
하나의 옵션에서 사전 것입니다 Type
에 Action
(또는 다른 대리자를). 유형에 따라 조치를 찾은 후 실행하십시오. 나는 지금까지 공장에서 이것을 사용했습니다.
답변
함께 JaredPar의 대답은 내 머리의 뒤쪽에, 내가 쓴 자신의 변형 TypeSwitch
사용이 더 좋은 구문에 대한 추론을 입력 클래스를 :
class A { string Name { get; } }
class B : A { string LongName { get; } }
class C : A { string FullName { get; } }
class X { public string ToString(IFormatProvider provider); }
class Y { public string GetIdentifier(); }
public string GetName(object value)
{
string name = null;
TypeSwitch.On(value)
.Case((C x) => name = x.FullName)
.Case((B x) => name = x.LongName)
.Case((A x) => name = x.Name)
.Case((X x) => name = x.ToString(CultureInfo.CurrentCulture))
.Case((Y x) => name = x.GetIdentifier())
.Default((x) => name = x.ToString());
return name;
}
Case()
방법 의 순서 는 중요합니다.
내 TypeSwitch
수업에 대한 전체 주석 코드를 가져옵니다 . 이것은 작동하는 약식 버전입니다.
public static class TypeSwitch
{
public static Switch<TSource> On<TSource>(TSource value)
{
return new Switch<TSource>(value);
}
public sealed class Switch<TSource>
{
private readonly TSource value;
private bool handled = false;
internal Switch(TSource value)
{
this.value = value;
}
public Switch<TSource> Case<TTarget>(Action<TTarget> action)
where TTarget : TSource
{
if (!this.handled && this.value is TTarget)
{
action((TTarget) this.value);
this.handled = true;
}
return this;
}
public void Default(Action<TSource> action)
{
if (!this.handled)
action(this.value);
}
}
}
답변
수퍼 클래스 (S)를 작성하고 A와 B를 상속하십시오. 그런 다음 모든 서브 클래스가 구현해야하는 추상 메소드를 S에 선언하십시오.
이렇게하면 “foo”메소드가 서명을 Foo (S o)로 변경하여 형식을 안전하게 만들 수 있으며, 그 추악한 예외를 던질 필요가 없습니다.
답변
C # 7 이상에서 패턴 일치를 사용할 수 있습니다.
switch (foo.GetType())
{
case var type when type == typeof(Player):
break;
case var type when type == typeof(Address):
break;
case var type when type == typeof(Department):
break;
case var type when type == typeof(ContactType):
break;
default:
break;
}
답변
명확하게 스스로를 시도하지 않고 메서드에 과부하가 걸리는 것입니다. 지금까지 대부분의 답변은 미래의 서브 클래스를 고려하지 않았기 때문에 나중에 끔찍한 유지 보수 문제가 발생할 수 있습니다.