361 votes

Est-il une meilleure alternative que de ce "de type"?

Vu que C# ne peut pas basculer sur un Type (dont je déduis n'était pas ajouté comme un cas spécial parce que c'est une des relations de dire que plus d'un distinct cas peuvent s'appliquer), est-il un meilleur moyen de simuler la commutation sur le type que cela?

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

340voto

Zachary Yates Points 4952

Vous pouvez changer le type de nom

switch(o.GetType().Name)
{
  case "AType":
    break;
}

Juste une pensée, j'espère que ça aide!

285voto

JaredPar Points 333733

La commutation sur les types est en manque définitivement de C#. Pour ce faire sans un grand if/else if/else, vous aurez besoin de travailler avec une structure différente. J'ai écrit un billet de blog un certain temps de retour détaillant la façon de construire un TypeSwitch structure.

http://blogs.msdn.com/jaredpar/archive/2008/05/16/switching-on-types.aspx

Version courte: TypeSwitch est conçu pour empêcher redondant de moulage et de donner une syntaxe similaire à une normale de commutateur de cas. Par exemple, voici TypeSwitch en action sur un formulaire Windows standard de l'événement

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

Le code pour TypeSwitch est en fait assez petite et peut facilement être mis dans votre projet.

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

102voto

Jon Skeet Points 692016

Une option est d'avoir un dictionnaire à partir d' Type de Action (ou son délégué). Regardez l'action fondée sur le genre, et puis l'exécuter. J'ai utilisé ce pour les usines avant maintenant.

49voto

Virtlink Points 12475

Avec JaredPar la réponse de à l'arrière de ma tête, j'ai écrit une variante de son TypeSwitch classe qui utilise l'inférence de type pour une meilleure syntaxe:

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

Notez que l'ordre de l' Case() méthodes est important.

Obtenir le plein et le code commenté pour ma TypeSwitch classe. Ce travail est une version abrégée:

public static class TypeSwitch
{
    public static Switch<TSource> On<TSource>(TSource value)
    {
        return new Switch<TSource>(value);
    }

    public class Switch<TSource>
    {
        private 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 (!handled)
            {
                var sourceType = value.GetType();
                var targetType = typeof(TTarget);
                if (targetType.IsAssignableFrom(sourceType))
                {
                    action((TTarget)value);
                    handled = true;
                }
            }

            return this;
        }

        public void Default(Action<TSource> action)
        {
            if (!handled)
                action(value);
        }
    }
}

14voto

Pablo Fernandez Points 32003

Créer une super-classe (S) et faire Un et B hériter d'elle. Puis déclarer une méthode abstraite sur S que chaque sous-classe doit implémenter.

Ce faisant les "foo" méthode peut également modifier sa signature à Toto(S o), il est de type sécurisé, et vous n'avez pas besoin de jeter de ce vilain exception.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X