【问题标题】:Private delegates in C#C# 中的私有委托
【发布时间】:2015-07-14 01:11:46
【问题描述】:

我正在使用一个包含多个私有方法变体的类。目前,我正在使用枚举在这样的公开方法中选择合适的枚举。

public class MyClass
{
    public enum MyEnum { Type1, Type2, Type3, Type4 };

    private MyEnum _type;


    public MyClass(MyEnum type)
    {
        Type = type;
    }


    public MyEnum Type
    {
        get { return _type; }
        set { _type = value; }
    }


    public int Function(int x, int y)
    {
        switch(_type)
        {
            case MyEnum.Type1:
                return Function1(x,y);
            case MyEnum.Type2:
                return Function2(x,y);
            case MyEnum.Type3:
                return Function3(x, y);
            case MyEnum.Type4:
                return Function4(x, y);
        }
    }


    private int Function1(int x, int y)
    {
        // function variant 1
    }


    private int Function2(int x, int y)
    {
        // function variant 2
    }


    private int Function3(int x, int y)
    {
        // function variant 3
    }


    private int Function4(int x, int y)
    {
        // function variant 4
    }

}

这很好用,但我想知道是否最好使用一个私有委托,该委托会在枚举更改时更新。尤其是在这种情况下,公共方法的调用频率要比枚举 setter 高得多。

public class MyClass
{
    public enum MyEnum { Type1, Type2, Type3, Type4 };

    private Func<int, int, int> _function;
    private MyEnum _type;


    public MyClass(MyEnum type)
    {
        Type = type;
    }


    public MyEnum Type
    {
        get { return _type; }
        set
        {
            _type = value;
            OnTypeChange();
        }
    }


    private void OnTypeChange()
    {
        switch (_type)
        {
            case MyEnum.Type1:
                _function = Function1;
                return;
            case MyEnum.Type2:
                _function = Function2;
                return;
            case MyEnum.Type3:
                _function = Function3;
                return;
            case MyEnum.Type4:
                _function = Function4;
                return;
        }
    }


    public int Function(int x, int y)
    {
        return _function(x, y);
    }


    private int Function1(int x, int y)
    {
        // function variant 1
    }


    private int Function2(int x, int y)
    {
        // function variant 2
    }


    private int Function3(int x, int y)
    {
        // function variant 3
    }


    private int Function4(int x, int y)
    {
        // function variant 4
    }

}

我想我只是在寻找有关该主题的一些传统智慧。这种事情在野外通常是如何处理的?

【问题讨论】:

  • 基本上,我认为大多数人都会同意,任何删除该枚举的解决方案都是可取的。这是一个维护点。

标签: c# delegates


【解决方案1】:

您的第二个选项在技术上更好,因为您不必在每次调用公共方法时都进行切换。

第一个更具可读性。

实际上,在枚举上切换行为是一个体面的危险信号。通常你会继承MyClass 并使用多态性来获得所需的行为。在你的情况下,我肯定会考虑这样做。 “在野外”,这可能是我会使用的方法。

【讨论】:

  • 虽然子类化也是一种解决方案,但它可能会导致不希望的类型爆炸和更多样板。如果设计正确,我可能会问自己:当有人用myInstance.X = Mode42; myInstance.Function(a,b); 之类的东西更改该类型时,如果他们可以写:myInstance = new Mode42Processor(); myInstance.Function(a,b); 好处是不需要维护枚举.是否使用所有模式处理器类实现的接口或使用其他方法来定义所有这些仍然是开放的。
  • @BitTickler 虽然是这样,但这个类根本不是“对扩展开放,对修改关闭”。枚举改变这样的方法调用对我来说很臭。我将授予类型爆炸的潜力,但它似乎比这里的替代方案更好。当然,界面在这里当然是有益的
  • @BradleyDotNET 我在下面给出了一个简短的示例,说明如何在不违反 OCP 的情况下解决 OP 的问题并仍然保持对类型层次结构的大部分控制。我认为这可能是您在回答中回避的一种尝试。
【解决方案2】:

按照 BradleyDotNET 的目标,有一些方法可以遵循开放/封闭原则以及利用 .NET 强大功能的良好设计实践。这是一个简单的示例,说明如何实现您的要求,使其易于扩展和易于维护。

public enum MyEnum
{
    Value1, Value2, Value3, Value4
}

[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class MyClassHandlerAttribute : Attribute
{
    public MyEnum Handles { get; private set; }
    public MyClassHandlerAttribute(MyEnum handles) { Handles = handles; }
}

public abstract class MyClass
{
    public abstract int Function(int x, int y);
}

public static class MyClassFactory
{
    public static MyClass Create(MyEnum type)
    {
        var handler = Assembly.GetExecutingAssembly().GetTypes().Where(t =>
        {
            var a = t.GetCustomAttribute<MyClassHandlerAttribute>();
            if (a != null && a.Handles == type)
                return true;
            return false;
        }).FirstOrDefault();

        if (handler != null)
            return Activator.CreateInstance(handler) as MyClass;

        return null;
    }
}

[MyClassHandler(MyEnum.Value1)]
public sealed class MyClassType1 : MyClass
{
    public int Function(int x, int y) { return x * y; }
}

[MyClassHandler(MyEnum.Value2)]
public sealed class MyClassType2 : MyClass
{
    public int Function(int x, int y) { return x * x + y; }
}

您甚至可以使子类具有内部默认构造函数,这样程序集之外的任何人都不会创建任何类或子类的类型。

如果您想了解代码,您可以使用 MyEnum 类型(可以在 MyClass 内移动,但如果您不得不添加它会违反 OCP),因此它在外部定义班级。

然后您有一个自定义属性MyClassHandlerAttribute,因此您可以将处理程序标记为由工厂类“自动发现”。

然后你就有了工厂,它使用类型检查来为你的枚举加载处理程序。它非常简单,它使用自定义属性查看程序集中的所有类型,如果它处理该枚举值,则返回它。 (可能有人对此有一个更流畅的 LINQ 查询,但我很快就做到了)

然后你就有了你的子类,易于维护,没有代码异味,简单的短代码单元。

【讨论】:

    【解决方案3】:

    如果你真的不想子类化,另一种选择是使用查找表 (LUT):

        delegate int FunctionDelegate(int x, int y);
    
        enum FunctionName { Function1, Function2, Function3 };
    
        class FunctionItem
        {
            public FunctionItem(FunctionName functionName, FunctionDelegate del)
            {
                FunctionName = functionName;
                Delegate = del;
            }
    
            public FunctionName FunctionName
            {
                private set;
                get;
            }
    
            public FunctionDelegate Delegate
            {
                private set;
                get;
            }
        }
    
        static readonly FunctionItem[] LUT = new FunctionItem[]
        {
            new FunctionItem(FunctionName.Function1, Method1),
            new FunctionItem(FunctionName.Function2, Method2),
            new FunctionItem(FunctionName.Function3, Method3)
        };
    
        // Fragment of lookup:
    
        FunctionItem f = LUT[function];
        Debug.Assert(f.Function == function);
        int result = f.Delegate(...); // Invoke delegate
    

    不完整,但你明白了。作为一个额外的优势,您可以扩展 FunctionItem,为每个函数添加额外的属性,如 NameDescription 等。

    至于合适的 OO 替代方案,请查看 Strategy 设计模式(= 子类化)。

    这很有趣,因为子类化的工作原理是一样的……vtable实际上是一个LUT,而FunctionItem变成了一个抽象基类。

    【讨论】:

      猜你喜欢
      • 2021-02-12
      • 2013-03-16
      • 2023-04-03
      • 2012-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-20
      相关资源
      最近更新 更多