416 Stimmen

Gibt es eine bessere Alternative zu diesem "Einschalttyp"?

Da C# nicht in der Lage ist switch bei einem Typ (der, wie ich annehme, nicht als Sonderfall hinzugefügt wurde, weil is Beziehungen bedeuten, dass mehr als eine bestimmte case gelten könnte), gibt es eine bessere Möglichkeit, das Einschalten des Typs zu simulieren als diese?

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());
    }
}

19voto

PilgrimViis Punkte 1361

C# 8 Erweiterungen des Musterabgleichs machten es möglich, dies wie folgt zu tun. In einigen Fällen tun es die Arbeit und mehr prägnant.

        public Animal Animal { get; set; }
        ...
        var animalName = Animal switch
        {
            Cat cat => "Tom",
            Mouse mouse => "Jerry",
            _ => "unknown"
        };

14voto

Pablo Fernandez Punkte 98441

Erstellen Sie eine Oberklasse (S) und lassen Sie A und B von dieser erben. Dann deklarieren Sie eine abstrakte Methode auf S, die jede Unterklasse implementieren muss.

Dadurch kann die "foo"-Methode auch ihre Signatur in Foo(S o) ändern, was sie typsicher macht, und Sie müssen diese hässliche Ausnahme nicht auslösen.

11voto

Serge Intern Punkte 2309

Ja, dank C# 7 kann das erreicht werden. Hier ist, wie es getan wird (mit Ausdrucksmuster ) :

switch (o)
{
    case A a:
        a.Hop();
        break;
    case B b:
        b.Skip();
        break;
    case C _: 
        return new ArgumentException("Type C will be supported in the next version");
    default:
        return new ArgumentException("Unexpected type: " + o.GetType());
}

8voto

Paul Batum Punkte 7845

Wenn Sie C# 4 verwenden würden, könnten Sie die neuen dynamischen Funktionen nutzen, um eine interessante Alternative zu schaffen. Ich behaupte nicht, dass dies besser ist, es scheint sogar sehr wahrscheinlich, dass es langsamer wäre, aber es hat eine gewisse Eleganz.

class Thing
{

  void Foo(A a)
  {
     a.Hop();
  }

  void Foo(B b)
  {
     b.Skip();
  }

}

Und die Verwendung:

object aOrB = Get_AOrB();
Thing t = GetThing();
((dynamic)t).Foo(aorB);

Der Grund dafür ist, dass ein dynamischer Methodenaufruf in C# 4 seine Überladungen zur Laufzeit und nicht zur Kompilierungszeit auflöst. Ich habe ein wenig mehr über diese Idee geschrieben ganz aktuell . Ich möchte noch einmal betonen, dass dies wahrscheinlich schlechter funktioniert als alle anderen Vorschläge, ich biete es einfach aus Neugierde an.

7voto

Edward Ned Harvey Punkte 5630

Für eingebaute Typen können Sie die TypeCode-Aufzählung verwenden. Bitte beachten Sie, dass GetType() etwas langsam ist, aber wahrscheinlich in den meisten Situationen nicht relevant ist.

switch (Type.GetTypeCode(someObject.GetType()))
{
    case TypeCode.Boolean:
        break;
    case TypeCode.Byte:
        break;
    case TypeCode.Char:
        break;
}

Für benutzerdefinierte Typen können Sie Ihre eigene Aufzählung und entweder eine Schnittstelle oder eine Basisklasse mit abstrakten Eigenschaften oder Methoden erstellen...

Abstrakte Klasse Implementierung der Eigenschaft

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public abstract class Foo
{
    public abstract FooTypes FooType { get; }
}
public class FooFighter : Foo
{
    public override FooTypes FooType { get { return FooTypes.FooFighter; } }
}

Implementierung der Methode der abstrakten Klasse

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public abstract class Foo
{
    public abstract FooTypes GetFooType();
}
public class FooFighter : Foo
{
    public override FooTypes GetFooType() { return FooTypes.FooFighter; }
}

Interface-Implementierung der Eigenschaft

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public interface IFooType
{
    FooTypes FooType { get; }
}
public class FooFighter : IFooType
{
    public FooTypes FooType { get { return FooTypes.FooFighter; } }
}

Interface-Implementierung der Methode

public enum FooTypes { FooFighter, AbbreviatedFool, Fubar, Fugu };
public interface IFooType
{
    FooTypes GetFooType();
}
public class FooFighter : IFooType
{
    public FooTypes GetFooType() { return FooTypes.FooFighter; }
}

Einer meiner Kollegen hat mir auch gerade davon erzählt: Das hat den Vorteil, dass man es für buchstäblich alle Arten von Objekten verwenden kann, nicht nur für solche, die man selbst definiert. Es hat den Nachteil, dass es ein bisschen größer und langsamer ist.

Definieren Sie zunächst eine statische Klasse wie diese:

public static class TypeEnumerator
{
    public class TypeEnumeratorException : Exception
    {
        public Type unknownType { get; private set; }
        public TypeEnumeratorException(Type unknownType) : base()
        {
            this.unknownType = unknownType;
        }
    }
    public enum TypeEnumeratorTypes { _int, _string, _Foo, _TcpClient, };
    private static Dictionary<Type, TypeEnumeratorTypes> typeDict;
    static TypeEnumerator()
    {
        typeDict = new Dictionary<Type, TypeEnumeratorTypes>();
        typeDict[typeof(int)] = TypeEnumeratorTypes._int;
        typeDict[typeof(string)] = TypeEnumeratorTypes._string;
        typeDict[typeof(Foo)] = TypeEnumeratorTypes._Foo;
        typeDict[typeof(System.Net.Sockets.TcpClient)] = TypeEnumeratorTypes._TcpClient;
    }
    /// <summary>
    /// Throws NullReferenceException and TypeEnumeratorException</summary>
    /// <exception cref="System.NullReferenceException">NullReferenceException</exception>
    /// <exception cref="MyProject.TypeEnumerator.TypeEnumeratorException">TypeEnumeratorException</exception>
    public static TypeEnumeratorTypes EnumerateType(object theObject)
    {
        try
        {
            return typeDict[theObject.GetType()];
        }
        catch (KeyNotFoundException)
        {
            throw new TypeEnumeratorException(theObject.GetType());
        }
    }
}

Und dann können Sie es so verwenden:

switch (TypeEnumerator.EnumerateType(someObject))
{
    case TypeEnumerator.TypeEnumeratorTypes._int:
        break;
    case TypeEnumerator.TypeEnumeratorTypes._string:
        break;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X