鉴于c#不能切换类型(我收集到没有作为特殊情况添加,因为关系意味着可能应用多个不同的情况),有没有更好的方法来模拟类型切换?

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

当前回答

我使用

    public T Store<T>()
    {
        Type t = typeof(T);

        if (t == typeof(CategoryDataStore))
            return (T)DependencyService.Get<IDataStore<ItemCategory>>();
        else
            return default(T);
    }

其他回答

一种选择是使用一个从Type到Action(或其他委托)的字典。根据类型查找操作,然后执行它。我以前在工厂用过这个。

c#中绝对没有类型切换(更新:在c# 7 / VS 2017中支持类型切换-参见Zachary Yates的回答)。为了不使用大量的if/else if/else语句,您需要使用不同的结构。前段时间我写了一篇博文,详细介绍了如何构建TypeSwitch结构。

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

短版本:TypeSwitch的设计目的是防止冗余强制转换,并提供类似于普通switch/case语句的语法。例如,下面是TypeSwitch在一个标准Windows窗体事件上的操作

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

我将创建一个接口,其名称和方法名称对您的交换机有意义,让我们分别调用它们:IDoable,告诉实现void Do()。

public interface IDoable
{
    void Do();
}

public class A : IDoable
{
    public void Hop() 
    {
        // ...
    }

    public void Do()
    {
        Hop();
    }
}

public class B : IDoable
{
    public void Skip() 
    {
        // ...
    }

    public void Do()
    {
        Skip();
    }
}

修改方法如下:

void Foo<T>(T obj)
    where T : IDoable
{
    // ...
    obj.Do();
    // ...
}

至少这样你在编译时是安全的,而且我认为在性能方面它比在运行时检查类型要好。

我同意Jon关于类名的操作散列。如果你想保留你的模式,你可能想要考虑使用"as"结构:

A a = o as A;
if (a != null) {
    a.Hop();
    return;
}
B b = o as B;
if (b != null) {
    b.Skip();
    return;
}
throw new ArgumentException("...");

区别在于当你使用if (foo is Bar) {((Bar)foo).Action();}你做了两次类型转换。现在编译器可能会优化,只做一次工作-但我不会指望它。

根据c# 7.0规范,你可以在开关的情况下声明一个局部变量的作用域:

object a = "Hello world";
switch (a)
{
    case string myString:
        // The variable 'a' is a string!
        break;
    case int myInt:
        // The variable 'a' is an int!
        break;
    case Foo myFoo:
        // The variable 'a' is of type Foo!
        break;
}

这是做这件事的最佳方式,因为它只涉及强制转换和栈上推操作,这是解释器可以运行的最快的操作之一,只是在位操作和布尔条件之前。

与使用Dictionary<K, V>相比,这里占用的内存更少,计算量基本为零。

另一方面,这应该和使用if语句链一样快(如果不是更快的话):

object a = "Hello world";
if (a is string)
{
    // The variable 'a' is a string!
} else if (a is int)
{
    // The variable 'a' is an int!
} // etc.