根据类型处理不同的逻辑
本文关键字:类型 处理 | 更新日期: 2023-09-27 18:07:58
我有class Foo<T>
和一个返回类型T
和签名M(Bar bar)
的实例方法Foo<T>.M
。对T
(T : AbstractBaseClass
)有一个约束,因此我确定T
具有属性T.SomeProperty
(和无参数构造函数约束)。假设M
必须根据bar
和T
的具体类型设置T.SomeProperty
的值。我不希望我的代码看起来像
T t = new T();
if(typeof(T) == T1) {
T.SomeProperty = // some function of bar
}
else if(typeof(T) == T2) {
T.SomeProperty = // some function of bar
}
else if(typeof(T) == T3) {
T.SomeProperty == // some function of bar
}
我不想在T
上放置一个实例方法,它从Bar
中获取值来填充T.SomeProperty
,因为这会使我的T
s依赖于一些我不想让它们依赖的东西。
我最好的选择是什么?
我的意思是:
class AbstractBaseClass {
public int SomeProperty { get; set; }
}
class Foo<T> where T : AbstractBaseClass, new() {
public T M(Bar bar) {
T t = new T();
t.SomeProperty = // function of bar, typeof(T)
return t;
}
}
如何编写M
,但避免类型参数T
上的逐类型逻辑?
这个怎么样?这是Corey的想法:
interface ISomePropertyStrategy<T> {
int GetSomeProperty(Bar bar);
}
class SomePropertyStrategyForConcreteClass1 :
ISomePropertyStrategy<ConcreteClass1> {
public int GetSomeProperty(Bar bar) { return bar.MagicValue + 73; }
}
class SomePropertyStrategyForConcreteClass2 :
ISomePropertyStrategy<ConcreteClass2> {
public int GetSomeProperty(Bar bar) { return bar.MagicValue - 12; }
}
class Foo<T> where T : AbstractBaseClass, new() {
private readonly ISomePropertyStrategy<T> strategy;
public Foo<T>(ISomePropertyStrategy<T> strategy) {
this.stragety = strategy;
}
public T M(Bar bar) {
T t = new T();
t.SomeProperty = this.strategy.GetSomeProperty(bar);
return t;
}
}
我唯一不喜欢的是它使用了泛型接口,泛型类型参数永远不会出现在接口中。我记得我曾经看过Eric Lippert的评论,他说这不是一个好主意,但我不记得了。对不起。
所以你有这个:
class Foo<T>
where T : AbstractBaseClass, new()
{
T M( Bar bar )
{
T t = new T();
if ( typeof (T) == T1 )
{
t.SomeProperty = bar.SomeMethod();
}
else if ( typeof (T) == T2 )
{
t.SomeProperty = bar.SomeOtherMethod();
}
else if ( typeof (T) == T3 )
{
t.SomeProperty == bar.YetAnotherMethod();
}
}
}
你可以这样做:
T M( Bar bar, Func<object> barFunction )
{
T t = new T();
t.SomeProperty = barFunction();
}
它不需要与你的Bar方法紧密耦合。以下是Func<T>
委托的一些信息。
好的,这是一个完整的程序。出于示例的考虑,我将Bar作为某个类,它持有一个有趣的值(这里是100)。我吃了。M是一个例程,如果类型参数是ConcreteClass1,则要将73添加到Bar中的数字;如果类型参数为ConcreteClass2,则需要从Bar中的数字减去12。
接口IABCVisitor和虚拟方法AcceptVisitor(每个类一个)可能看起来开销很大,但好处是您只需要支付一次开销:一旦将此模式添加到类层次结构中,您就可以一次又一次地重用它,每当调用者想要基于类型执行自定义逻辑时。我希望下面的程序对你有意义。
using System;
using System.Diagnostics;
namespace ConsoleApplication33 {
public class Program {
public static void Main() {
var foo1=new Foo<ConcreteClass1>();
var foo2=new Foo<ConcreteClass2>();
var bar=new Bar(100);
var result1=foo1.M(bar);
var result2=foo2.M(bar);
Debug.Print("result1.SomeProperty="+result1.SomeProperty);
Debug.Print("result2.SomeProperty="+result2.SomeProperty);
}
}
//----------------------------------------------------------------------------
// these definitions can appear in project 1
// notice that project 1 does not have any dependencies on Bar
//----------------------------------------------------------------------------
/// <summary>
/// This interface needs a line for each class in the hierarchy
/// </summary>
public interface IABCVisitor<out T> {
T Visit(AbstractBaseClass x);
T Visit(ConcreteClass1 x);
T Visit(ConcreteClass2 x);
}
public abstract class AbstractBaseClass {
public int SomeProperty { get; set; }
/// <summary>
/// All of AbstractBaseClasses' children need to override this property
/// </summary>
public virtual T AcceptVisitor<T>(IABCVisitor<T> visitor) {
return visitor.Visit(this);
}
}
public class ConcreteClass1 : AbstractBaseClass {
public override T AcceptVisitor<T>(IABCVisitor<T> visitor) {
return visitor.Visit(this);
}
}
public class ConcreteClass2 : AbstractBaseClass {
public override T AcceptVisitor<T>(IABCVisitor<T> visitor) {
return visitor.Visit(this);
}
}
//----------------------------------------------------------------------------
// these definitions can appear in project 2
//----------------------------------------------------------------------------
public class Bar {
public int MagicValue { get; private set; }
public Bar(int magicValue) {
MagicValue=magicValue;
}
}
public class Foo<T> where T : AbstractBaseClass, new() {
public T M(Bar bar) {
T t=new T();
t.SomeProperty=t.AcceptVisitor(new CalculateTheRightValue(bar));
return t;
}
}
public class CalculateTheRightValue : IABCVisitor<int> {
private readonly Bar bar;
public CalculateTheRightValue(Bar bar) {
this.bar=bar;
}
public int Visit(AbstractBaseClass x) {
throw new NotImplementedException("not implemented for type "+x.GetType().Name);
}
public int Visit(ConcreteClass1 x) {
return bar.MagicValue+73;
}
public int Visit(ConcreteClass2 x) {
return bar.MagicValue-12;
}
这看起来像是一个非常经典的访问者模式的应用程序