C# 中的私有委托
Private delegates in C#
我正在使用一个 class,其中包含私有方法的多个变体。目前,我正在使用枚举 select 公开方法中的适当枚举。
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
}
}
这很好用,但我想知道我是否最好使用一个私有委托,它会在枚举更改时更新。特别是因为在这种情况下,public 方法将比枚举 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
}
}
我想我只是在寻找关于这个主题的一些传统智慧。这种事情在野外通常是如何处理的?
你的第二个选择技术上更好,因为你不必在每次调用 public 方法时都进行切换。
第一个更具可读性。
尽管如此,在所有现实中,枚举的切换行为是一个相当大的危险信号。通常您会子类化 MyClass
并使用多态性来获得所需的行为。对于您的情况,我肯定会考虑这样做。 "In the wild",这可能是我会使用的方法。
按照 BradleyDotNET 的目标,有一些方法可以遵循 open/closed 原则以及利用 .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; }
}
您甚至可以让 subclasses 具有内部默认构造函数,这样程序集之外的任何人都不会创建任何 class 或 subclass 的类型。
如果您想浏览代码,可以使用 MyEnum
类型(可以将其移到 MyClass
中,但如果您必须向其中添加,则会违反 OCP ), 所以它定义在 class.
之外
然后您有一个自定义属性 MyClassHandlerAttribute
,因此您可以将处理程序标记为 "auto-discovered" 由工厂 class。
然后你就有了工厂,它使用类型检查来为你的枚举加载处理程序。它非常简单,它使用自定义属性查看程序集中的所有类型,如果它处理该枚举值,它 returns 它。 (有人可能对此有更巧妙的 LINQ 查询,但我很快就做到了)
然后你有你的子 classes,易于维护,没有代码味道,简单的短代码单元。
如果你真的不想subclass,另一种方法是使用查找table (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
扩展每个函数的额外属性,例如 Name
、Description
等。
至于合适的 OO 替代方案,请查看 Strategy 设计模式 (= subclassing)。
这很有趣,因为 subclassing 的工作原理是一样的...vtable 实际上是一个 LUT,而 FunctionItem 变成了一个抽象基础 class.
我正在使用一个 class,其中包含私有方法的多个变体。目前,我正在使用枚举 select 公开方法中的适当枚举。
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
}
}
这很好用,但我想知道我是否最好使用一个私有委托,它会在枚举更改时更新。特别是因为在这种情况下,public 方法将比枚举 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
}
}
我想我只是在寻找关于这个主题的一些传统智慧。这种事情在野外通常是如何处理的?
你的第二个选择技术上更好,因为你不必在每次调用 public 方法时都进行切换。
第一个更具可读性。
尽管如此,在所有现实中,枚举的切换行为是一个相当大的危险信号。通常您会子类化 MyClass
并使用多态性来获得所需的行为。对于您的情况,我肯定会考虑这样做。 "In the wild",这可能是我会使用的方法。
按照 BradleyDotNET 的目标,有一些方法可以遵循 open/closed 原则以及利用 .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; }
}
您甚至可以让 subclasses 具有内部默认构造函数,这样程序集之外的任何人都不会创建任何 class 或 subclass 的类型。
如果您想浏览代码,可以使用 MyEnum
类型(可以将其移到 MyClass
中,但如果您必须向其中添加,则会违反 OCP ), 所以它定义在 class.
然后您有一个自定义属性 MyClassHandlerAttribute
,因此您可以将处理程序标记为 "auto-discovered" 由工厂 class。
然后你就有了工厂,它使用类型检查来为你的枚举加载处理程序。它非常简单,它使用自定义属性查看程序集中的所有类型,如果它处理该枚举值,它 returns 它。 (有人可能对此有更巧妙的 LINQ 查询,但我很快就做到了)
然后你有你的子 classes,易于维护,没有代码味道,简单的短代码单元。
如果你真的不想subclass,另一种方法是使用查找table (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
扩展每个函数的额外属性,例如 Name
、Description
等。
至于合适的 OO 替代方案,请查看 Strategy 设计模式 (= subclassing)。
这很有趣,因为 subclassing 的工作原理是一样的...vtable 实际上是一个 LUT,而 FunctionItem 变成了一个抽象基础 class.