C# 基于类型处理不同的逻辑
我有C# 基于类型处理不同的逻辑,c#,C#,我有类Foo和一个实例方法Foo.M,返回类型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(typeo
类Foo
和一个实例方法Foo.M
,返回类型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;
}
}
我唯一不喜欢的是它使用了一个泛型接口,而泛型类型参数从未出现在接口中。我想我曾经看到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();
}
}
}
class-Foo
其中T:AbstractBaseClass,new()
{
TM(巴)
{
T=新的T();
if(类型(T)=T1)
{
t、 SomeProperty=bar.SomeMethod();
}
else if(类型(T)=T2)
{
t、 SomeProperty=bar.SomeOtherMethod();
}
否则如果(类型(T)=T3)
{
t、 SomeProperty==bar.YetAnotherMethod();
}
}
}
您可以这样做:
T M( Bar bar, Func<object> barFunction )
{
T t = new T();
t.SomeProperty = barFunction();
}
TM(条形、函数条形)
{
T=新的T();
t、 SomeProperty=barFunction();
}
它不需要与Bar方法紧密耦合。在
Func
委托上。好的,这是一个完整的程序。出于示例的考虑,我认为Bar是一个包含有趣值的类(这里是100)。我认为foo.M是一个例程,如果类型参数是ConcreteClass1,它想在条内的数字上加73;如果类型参数是ConcreteClass2,它将希望从条内的数字中减去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;
}
使用系统;
使用系统诊断;
命名空间控制台应用程序33{
公共课程{
公共静态void Main(){
var foo1=新的Foo();
var foo2=新的Foo();
var棒=新棒(100);
var result1=foo1.M(巴);
var result2=foo2.M(bar);
Debug.Print(“result1.SomeProperty=“+result1.SomeProperty”);
Debug.Print(“result2.SomeProperty=“+result2.SomeProperty”);
}
}
//----------------------------------------------------------------------------
//这些定义可以出现在项目1中
//请注意,项目1对Bar没有任何依赖关系
//----------------------------------------------------------------------------
///
///此接口需要层次结构中的每个类对应一行
///
公共接口IABCVisitor{
T访视(抽象基类x);
T访问(1 x类);
T访问(2 x类);
}
公共抽象类AbstractBaseClass{
公共int SomeProperty{get;set;}
///
///所有AbstractBaseClass的子类都需要重写此属性
///
公共虚拟T接受访问者(IABCVisitor访问者){
回访者。参观(本);
}
}
公共类ConcreteClass1:AbstractBaseClass{
公共覆盖T接受访问者(IABCVisitor访问者){
回访者。参观(本);
}
}
公共类ConcreteClass2:AbstractBaseClass{
公共覆盖T接受访问者(IABCVisitor访问者){
回访者。参观(本);
}
}
//----------------------------------------------------------------------------
//这些定义可以出现在项目2中
//----------------------------------------------------------------------------
公共类酒吧{
public int MagicValue{get;private set;}
公共酒吧(int magicValue){
MagicValue=MagicValue;
}
}
公共类Foo,其中T:AbstractBaseClass,new(){
公共T M(酒吧){
T=新的T();
t、 SomeProperty=t.AcceptVisitor(新的CalculateRightValue(bar));
返回t;
}
}
公共类CalculateRightValue:IABCVisitor{
私人只读条;
公共CalculateRightValue(条形){
这个.bar=bar;
}
公共int访问(AbstractBaseClass x){
抛出新的NotImplementedException(“未为类型“+x.GetType().Name”实现);
}
公众参观(1 x类){
返回条MagicValue+73;
}
公共int访问(ConcreteClass2 x){
返回条MagicValue-12;
}
请解释,我不懂。您可以在AbstractBaseClass中有一个方法,类似于Apply(b栏)当然,这会为您设置一些可以重载的属性……或者接受像Func这样的投影。@詹姆斯·迈克尔·黑尔:这使得AbstractBaseClass
依赖于Bar
,这是我不想要的。我明白了,然后我会在我的评论中使用投影模型,这与保罗下面的回答类似。您将无法使用它从泛型类型转换而来,因此您不能依赖未在基类中定义的具体方法。那么,为什么有一个带有参数的接口没有任何用途?如果您只是从ISomePropertyStrategy中删除类型参数,您的程序会发生什么变化?不完全如此。Bar
上没有生成这些值取决于o
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;
}