优雅的课堂设计解决方案

时间:2012-07-04 09:08:50

标签: c# design-patterns

我在 C#。

中有以下设计问题编程应用程序

我有 A B 这两个派生自C 。我不能改变他们的定义,因为它们在外部装配中定义,并且也定义为部分

我想要实现的是基于天气提供的功能不同 C对象的类型为 A或B 。当然我不想使用if语句比较提供的对象的运行时类型。是否可以使用扩展方法?我不这么认为。 有解决方案吗:)

7 个答案:

答案 0 :(得分:2)

使用泛型可以使用扩展方法。 可能有多种方法,但这种方法最简单。虽然你确实得到 if

public static void Foo<T>(this T objectC)
     where T: C
{
     if(typeof(T)==typeof(B){ //or for runtime check:     if(objectC is B)
          //specific
     }
}
然后,您可以在任何A或B的实例上调用Foo。

你提到你不想要if语句,但是我不确定你在哪个方面试图避免这种情况?完全避免它的唯一方法是使用2个扩展方法,一个用于A,一个用于B,(反过来可以调用C的常用方法),但我认为你试图避免多种扩展方法? / p>

编辑如果您绝对想要阻止if,则必须使用多个扩展方法,如Frederik的帖子所示。您也可以为基类添加扩展,只有在编译期间未知类型时才会调用该扩展。但那仍然需要一个if;)

public static void Foo(this A a)
{
}

public static void Foo(this B b)
{
}

public static void Foo(this C c)
{
    if(c is A)
       Foo((A)c);
    else if(c is B)
       Foo((B)c);
    else
       throw new NotSupportedException(c.GetType().FullName);
}

如果在编译时始终知道类型,则可以简单地使用A en B的2种扩展方法。

答案 1 :(得分:0)

你可以试试这个:

public static class CExtensions {
     public static void DoIt( this B foo ) {}

     public static void DoIt( this A foo ) {}
}

但是,我认为这不会起作用:

C x = new A();
x.DoIt();

我认为它不会编译,但是,我现在无法测试它。

答案 2 :(得分:0)

纯粹从可行性的角度来看,可以使用反射和扩展方法。在您的应用程序中是否有意义,您应该判断。这是解决方案......

class C
{
}

class A : C
{
}

class B : C
{
}

static class Extender
{
    public static void M(this B b)
    {
        Console.WriteLine(" Extension method on B");
    }

    public static void M(this A a)
    {
        Console.WriteLine(" Extension method on A");
    }
}

static void Main(string[] args)
    {
        C c = new A();// The actual instance here will be created using some factory.
        object instance = Activator.CreateInstance(c.GetType());
        Type typeToFind = c.GetType();

        Type typeToQuery = typeof(Extender);

        var query = from method in typeToQuery.GetMethods(BindingFlags.Static
                        | BindingFlags.Public | BindingFlags.NonPublic)
                    where method.IsDefined(typeof(ExtensionAttribute), false)
                    where method.GetParameters()[0].ParameterType == typeToFind
                    select method;
      // You would be invoking the method based on its name. This is just a quick demo. 
        foreach (MethodInfo m in query)
        {
            m.Invoke(instance, new object[] { instance });
        }

    }

答案 3 :(得分:0)

对我而言,这感觉就像设计中的一个缺陷。 如果你不能在不知道运行时类型的情况下覆盖和更改功能的两个子类有一个公共类,那么你明显会遇到整体设计的一些问题。

根据运行时类型而有所不同的功能是否真的存在于这些类中?

答案 4 :(得分:0)

你可以这样做。使用您需要的不同方法创建一个类。在同一个类中定义一个具有此方法签名的委托,并保留一个字典,其中键是类型,代码是值。最后,您可以为类C创建一个扩展方法,该方法使用正在执行方法本身的对象的类型查看字典并执行正确的方法。像这样:

public static class CExtender
{
    private static void DoItA(C anA)
    {
        MessageBox.Show("A");
    }

    private static void DoItB(C aB)
    {
        MessageBox.Show("B");
    }

    private static void DoItC(C aC)
    {
        MessageBox.Show("C");
    }

    delegate void DoItDel(C aC);

    private static Dictionary<Type, DoItDel> _doItDels;

    private static Dictionary<Type, DoItDel> DoItDels
    {
        get 
        {
            if (_doItDels == null)
            {
                _doItDels = new Dictionary<Type, DoItDel>();
                _doItDels[typeof(A)] = new DoItDel(DoItA);
                _doItDels[typeof(B)] = new DoItDel(DoItB);
            }
            return _doItDels; 
        }
    }

    // the only public part is the extension method
    public static void DoIt(this C aC)
    {
        DoItDel aDel;
        if (DoItDels.TryGetValue(aC.GetType(), out aDel))
            aDel(aC);
        else
            DoItC(aC);
    }
}

答案 5 :(得分:0)

在这种情况下,您可以使用 Decorater模式

即。你可以有两个你自己的新类,比如说A1,B1都是从C派生的。(假设C不是密封的类。如果是这种情况解决方案会有点不同)。 A1和B1也需要相应地包裹A和B.您可以通过A1和B1的构造函数注入A和B的实例。

你也可以有一个共同的界面,其中A1和B1都是不受欢迎的。接口需要具有您需要A1和B1以自己的方式实现的方法。 (A1和B1可以根据需要将呼叫委托给A或B)

这样,在您的客户端代码中,您可以通过接口类型引用A1和B1实例,并执行接口中定义的公共操作,而不知道它们实际的具体实现是什么。

如果我不清楚,是否需要代码示例,请告诉我。

答案 6 :(得分:0)

好的,我找到了答案。 动态关键字是这里的线索。 我们可以写:

void Handle(C c)
{
   dynamic cc = c;  
   HandleSpecific(cc);   
}
void HandleSpecific(A a)
{
//Specific behavior A
}
void HandleSpecific(B b)
{
//Specific behavior B
}

缺点当然是 - 由于此处引入的运行时绑定以及轻微的性能损失,存在异常风险。