【问题标题】:Type casting with decorator pattern带有装饰图案的类型铸造
【发布时间】:2020-08-14 03:50:26
【问题描述】:

我有一个电机接口和各种特定于供应商的接口实现。

public interface IMotor
{
  void Home();
  void Move();
}

public class VenodorAMotor : IMotor
{
  public void Home()
  { /*home motor*/  }

  public void Move()
  { /*move motor*/  }     
}

我正在装饰 IMotor 以向其 home 方法添加一些功能。

public class HomeAdjustmentDecorator : IMotor
{
  private IMotor decoratedMtr;

  public static explicit operator VendorAMotor(HomeAdjustmentDecorator mtr) 
  { return (VendorAMotor)mtr.decoratedMtr; }

  public HomeAdjustmentDecorator(IMotor mtr)
  { 
    if(! mtr is VenodorAMotor)
      throw new Exception("HomeAdjustmentDecorator can only decorate VenodorAMotor objects");
    decoratedMtr = mtr; 
  }

  public void Home()
  {
    decoratedMtr.Home();
    double adjustment = ((VendorAMotor)myMotor).GetHomeAdjustment();//vendor-specific functionality
    Log(adjustment);
  }

  public void Move()
  { decoratedMtr.Move();  }  
}

我无法控制 VendorAMotor 的创建,所以我不能轻易使用继承。

问题在于现有代码已经将未修饰的 IMotor 强制转换为其实现类以访问其他供应商特定的功能。转换失败,因为 HomeAdjustmentDecorator 没有继承 VenodorAMotor。

IMotor myDecoratedMotor;//Implementation is HomeAdjustmentDecorator composed of VendorAMotor
...
((VendorAMotor)myDecoratedMotor).DoOtherVendorSpecificStuff();

我尝试过覆盖如图所示的强制转换运算符,但这不起作用。我得到一个 InvalidCastExeption:“无法将 'HomeAdjustmentDecorator' 类型的对象转换为 'VenodorAMotor'。”
一切编译正常。不执行显式转换运算符覆盖,不命中断点。问题是,这个强制转换是否没有执行?

最接近我在这里找到了一个类似的问题: Decoration with several interface 但这并没有太大帮助。

我想答案就在这里: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#user-defined-conversions 但我认为我遗漏了一些微妙的细节。

【问题讨论】:

  • 由于装饰器功能是特定于VenodorAMotor 实现的,您是否可以将必要的代码添加到VenodorAMotor 类中?
  • 假设myDecoratedMotor 是IMotor 并且您无法控制IMotor,您无法向其添加转换(只能将转换添加到他们自己的类)...所以你可能想澄清你到底想要达到什么目标,以及你愿意做些什么来达到目标​​......
  • @AlexeiLevenkov;您能否为我澄清一下您的陈述:“您无法为其添加转换(只能将转换添加到他们自己的班级)”?我正在尝试将IMotor,实现是HomeAdjustmentDecorator 转换为VenodorAMotor,HomeAdjustmentDecorator 由IMotor 组成(作为IMotor)。我控制“HomeAdjustmentDecorator”并且知道它是由我试图将其投射到的内容组成的。谢谢!
  • @user3532473 我没有得到你的评论。根据问题,它听起来像IMotor myDecoratedMotor; - 你没有机会根据你的限制让演员工作......事实上HomeAdjustmentDecorator myDecoratedMotor...; ((VenodorAMotor)myDecoratedMotor).DoOtherVendorSpecificStuff(); 可以很好地与你的代码一起工作......但比你想问的更好吗?跨度>
  • @AlexeiLevenkov。 ((VenodorAMotor)myDecoratedMotor).DoOtherVendorSpecificStuff();不工作。我想这正是我想问的 - 为什么它不起作用或者我错过了什么?似乎其他未决答案之一可能有它:“转换运算符不适用于接口,因为在编译时,没有具体类型可供(编译器)使用来查找此类运算符。”

标签: c# casting decorator


【解决方案1】:

正如其他人所提到的,将接口与类型转换混合到具体实现中是一种不好的做法。 注意:通常可以检查您的对象是否实现了一个或多个其他接口:

if (motor is IDisposable disposable)
    disposable.Dispose();

原因:接口的存在是为了定义对象的行为。接口是为了确保任何实现都适用于您的功能。当您检查具体实现时,您会破坏 Liskov 替换原则。

至于你原来的问题,你可以定义额外的接口:

public interface IMotorWithVendorStuff: IMotor
{
    DoOtherVendorSpecificStuff();
}

和VendorAMotor 的适配器以实现该接口:

public class VendorAMotorAdapter: IMotorWithVendorStuff
{
    // ...
}

您应该在将IMotor 转换为VendorAMotor 的所有地方使用IMotorWithVendorStuff。

【讨论】:

    【解决方案2】:

    我认为从接口转换为具体类来调用特定方法是一种不好的做法,而且正如您所见,它已经引起了问题。

    转换运算符不适用于接口,因为在编译时,没有具体类型可供(由编译器使用)来查找此类运算符。

    鉴于您无权访问 IMotor 的源代码,您可能希望至少集中演员表并使用 extension method 扩展 IMotor:

    public static class MotorExtensions
    {
        public static void DoOtherVendorSpecificStuff(this IMotor motor)
        {
            if (motor is HomeAdjustmentDecorator decorator)
            {
                // This calls implicit cast operator
                ((VendorAMotor)decorator).DoOtherVendorSpecificStuff();
                return;
            }
    
            ((VendorAMotor)motor).DoOtherVendorSpecificStuff();
        }
    }
    

    并将演员表更改为:

    IMotor motor = new VendorAMotor();
    IMotor decorated = new HomeAdjustmentDecorator(motor);
    
    // Both should work
    motor.DoOtherVendorSpecificStuff();
    decorated.DoOtherVendorSpecificStuff();
    

    【讨论】:

      猜你喜欢
      • 2018-11-23
      • 1970-01-01
      • 1970-01-01
      • 2014-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多