一般设计指导c#;发现我不必要地在方法之间传递对象

时间:2018-01-07 17:58:43

标签: c# oop

对不起它有点模糊,但它已经困扰了我几个星期。我找到了我所处理的每个项目,最终我发现了一个我认为是设计错误的东西,我很确定这是一个很好的方式。

定义从事件源序列化的类,如sinple json doc定义。让我们用各种定义的整数,bool和字符串来调用它的键类。我有多种方法可以利用这个,我发现我经常需要通过重载将这个类作为对象。所以方法a调用方法b,方法b不需要这些对象,但它调用方法c,这样做...在这种不好的做法中,我将这些“键”对象传递给方法b,仅用于方法c的可访问性。

我可能错过了一个主要的OOP基础:)任何指导或阅读都会受到赞赏,因为我用Google搜索了!!

public class Keys
{
    public child Detail { get; set; }
}

public class child
{
    public string instance { get; set; }
}

//my main entry point
public void FunctionHandler(Keys input, ILambdaContext context)
{
    methodA(input)
}

static void methodA(Keys input) 
{
   //some-other logic or test that doesn't need Keys object/class if (foo==bar) {proceed=true;}
   string foo = methodB(input)
}

static string methodB(Keys input) 
{
    //here i need Keys do do stuff and I return a string in this example
}

1 个答案:

答案 0 :(得分:3)

你做的不一定是坏或坏。请记住,在C#中,实际传递的是引用,而不是正确的对象,因此参数传递的开销非常小。

长调用链的主要缺点是程序逻辑可能比通常的可维护性问题更复杂。

有时你可以使用C#类型系统让编译器或运行时选择合适的函数。

当您为两种不同类型重载hours而不是定义method()methodA()时,会使用编译器。但它们通过参数类型区分,因此您需要不同的methodB()类型,这些类型可能(但不一定)相关:

Key

这是有限的好处;这些函数具有相同的名称只是语法糖,这清楚地表明它们具有相同的用途。

另一种,也许更优雅和多功能的技术是创建public class KeyA {/*...*/} public class KeyB {/*...*/} void method(KeyA kA) { /* do something with kA */ } void method(KeyB kB) { /* do something with kB */ } s的继承层次结构,每个层次结构都“知道”Key应该做什么。

您需要一个虚拟method的基类,它将被继承类覆盖。通常,基础是一个接口,只是声明有一些method,并且各种实现类型实现了适合它们的method()。这是一个有点冗长的示例,它使用虚拟method()方法,以便我们在控制台上看到一些内容。

值得注意的是,每个Output()调用Key的方法,将其作为参数传递给它;然后输出类接着回调调用对象的方法。这称为“Double Dispatch”,它将运行时多态性与编译时函数重载相结合。在编译时,对象及其具体类型未知;事实上,它们可以在以后实施(例如通过发明另一个OutputterI)。但是每个对象都知道在调用其回调函数(这里:Key)时该怎么做。

GetData()

示例输出:

using System;
using System.Collections.Generic;

namespace DoubleDispatch
{
    interface KeyI
    {   // They actually delegate that to an outputter
        void Output(); 
    }

    interface OutputterI
    {
        void Output(KeyA kA);
        void Output(KeyExtra kE);
        void Output(KeyI k); // whatever this does.
    }

    class KeyBase: KeyI
    {
        protected OutputterI o;

        public KeyBase(OutputterI oArg) { o = oArg; }

        // This will call Output(KeyI))
        public virtual void Output() { o.Output(this); }
    }

    class KeyA : KeyBase
    {
        public KeyA(OutputterI oArg) : base(oArg) { }

        public string GetAData() { return "KeyA Data"; }

        // This will compile to call Output(KeyA kA) because
        // we pass this which is known here to be of type KeyA
        public override void Output() { o.Output(this); }
    }

    class KeyExtra : KeyBase
    {
        public string GetEData() { return "KeyB Data"; }
        public KeyExtra(OutputterI oArg) : base(oArg) { }

        /** Some extra data which needs to be handled during output. */
        public string GetExtraInfo() { return "KeyB Extra Data"; }

        // This will, as is desired,
        // compile to call o.Output(KeyExtra) 
        public override void Output() { o.Output(this); }
    }

    class KeyConsolePrinter : OutputterI
    {
        // Note: No way to print KeyBase.
        public void Output(KeyA kA) { Console.WriteLine(kA.GetAData());  }
        public void Output(KeyExtra kE)
        {
            Console.Write(kE.GetEData() + ", ");
            Console.WriteLine(kE.GetExtraInfo());
        }
        // default method for other KeyI
        public void Output(KeyI otherKey) { Console.WriteLine("Got an unknown key type"); }

    }
    // similar for class KeyScreenDisplayer{...} etc.

    class DoubleDispatch
    {
        static void Main(string[] args)
        {

            KeyConsolePrinter kp = new KeyConsolePrinter();

            KeyBase b = new KeyBase(kp);
            KeyBase a = new KeyA(kp);
            KeyBase e = new KeyExtra(kp);

            // Uninteresting, direkt case: We know at compile time
            // what each object is and could simply call kp.Output(a) etc.
            Console.Write("base:\t\t");
            b.Output();

            Console.Write("KeyA:\t\t");
            a.Output();

            Console.Write("KeyExtra:\t");
            e.Output();

            List<KeyI> list = new List<KeyI>() { b, a, e };
            Console.WriteLine("\nb,a,e through KeyI:");

            // Interesting case: We would normally not know which
            // type each element in the vector has. But each type's specific
            // Output() method is called -- and we know it must have
            // one because that's part of the interface signature.
            // Inside each type's Output() method in turn, the correct
            // OutputterI::Output() for the given real type was 
            // chosen at compile time dpending on the type of the respective
            // "this"" argument.
            foreach (var k in list) { k.Output(); }
        }
    }
}