Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 优雅的类设计解决方案_C#_Design Patterns - Fatal编程技术网

C# 优雅的类设计解决方案

C# 优雅的类设计解决方案,c#,design-patterns,C#,Design Patterns,我在C#中有以下设计问题编程应用程序。 我有A和B两个类,它们都是从C派生出来的。我无法更改它们的定义,因为它们在外部组件中定义,并且未定义为部分 我试图实现的是根据天气来区分功能,前提是C对象属于A或B类型。当然,我不想使用if语句来比较所提供对象的运行时类型。扩展方法是否可行?我不这么认为。 有什么解决办法吗?:) 您可以尝试以下方法: public static class CExtensions { public static void DoIt( this B foo ) {

我在C#中有以下设计问题编程应用程序。

我有AB两个类,它们都是从C派生出来的。我无法更改它们的定义,因为它们在外部组件中定义,并且未定义为部分

我试图实现的是根据天气来区分功能,前提是C对象属于A或B类型。当然,我不想使用if语句来比较所提供对象的运行时类型。扩展方法是否可行?我不这么认为。 有什么解决办法吗?:)

您可以尝试以下方法:

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

     public static void DoIt( this A foo ) {}
}
但是,我认为这样做行不通:

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

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

通过使用泛型的扩展方法应该是可能的。 多种方法是可能的,但这一种是最简单的。虽然你确实得到了,如果


如果类型在编译时总是已知的,那么您可以简单地为一个en B使用两种扩展方法。

纯粹从可行性的角度来看,可以使用反射和扩展方法。在应用程序的上下文中,它是否有意义,是您应该判断的事情。解决办法来了

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

    }

对我来说,这感觉像是设计上的一个缺陷。 如果在不知道运行时类型的情况下,无法将一个公共类转换为两个子类(覆盖和更改功能),那么很明显,总体设计存在一些问题


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

您可以这样做。使用所需的不同方法创建一个类。在同一个类中,使用此方法的签名定义一个委托,并保留一个字典,其中键是委托的类型和值。最后,您可以为类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);
    }
}
公共静态类CExtender
{
私有静态void DoItA(C anA)
{
MessageBox.Show(“A”);
}
专用静态无效DoItB(C aB)
{
MessageBox.Show(“B”);
}
专用静态无效DoItC(C aC)
{
MessageBox.Show(“C”);
}
代表void DoItDel(C aC);
私有静态字典_doItDels;
私有静态字典DoItDels
{
得到
{
如果(_doItDels==null)
{
_doItDels=新字典();
_doItDels[typeof(A)]=新的DoItDel(DoItA);
_doItDels[typeof(B)]=新的DoItDel(DoItB);
}
返回_doItDels;
}
}
//唯一的公共部分是扩展方法
公共静态无效DoIt(此C aC)
{
多特德尔·阿德尔;
if(DoItDels.TryGetValue(aC.GetType(),out aDel))
阿德尔(aC);
其他的
DoItC(aC);
}
}

在这种情况下,您可以使用装饰器模式

i、 你可以有两个你自己的新类,比如A1,B1,都是从C派生的(前提是C不是一个密封的类。如果是这样的话,解决方案会有点不同)。A1和B1也需要相应地包裹A和B。您可以通过A1和B1的构造函数注入A和B的实例

您还可以有一个通用接口,A1和B1都可以实现。接口需要使用A1和B1以各自的方式实现的方法。(A1和B1可以根据需要将呼叫委托给A或B)

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

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

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

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

缺点当然是-由于此处介绍的运行时绑定和轻微的性能影响,可能会出现异常。

请说明您迄今为止取得的成果以及为什么这些成果不符合您的要求。为什么您需要知道C是A还是B?如何获取/创建A和B的实例?您是
new
还是通过某种工厂方法创建的?我不创建它们。让我们假设它们是给我的。一旦它们给了你,它们是否仍然存在,它们是否长寿?或者这些对象是作为参数传递给您在方法中执行操作,然后它们超出了范围?我认为这是最实用的解决方案。我同意if有点难看,但不是这样就是使用多种扩展方法。纯粹主义者可能会选择使用工厂创建装饰器类型的装饰器模式实现,如果您正在构建一个需要以开放-封闭原则方式扩展的框架(扩展方法方式违反OCP),这可能是合适的我不想使用if语句,因为我认为比较运行时类型不是一种好的做法:)现在担心好的做法有点晚了。在不看A和B的类型的情况下,你还能如何区分它们呢?但我如何知道哪些类型(A1或A2)需要共同创建呢?只有在运行时才知道它。
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);
    }
}
void Handle(C c)
{
   dynamic cc = c;  
   HandleSpecific(cc);   
}
void HandleSpecific(A a)
{
//Specific behavior A
}
void HandleSpecific(B b)
{
//Specific behavior B
}