没有接口的 sealed class 的复合模式
Composite pattern of sealed class that has no interface
假设我正在使用一个我无法控制 的库。该库公开了需要某些 class 参数的服务。 Class 标记为密封并且没有接口。
tl;dr:如何将 sealed class 重新实现为 interface?
代码示例:
using System;
namespace IDontHaveControlOverThis
{
// Note no interface and the class is being sealed
public sealed class ArgumentClass
{
public String AnyCall() => "ArgumentClass::AnyCall";
}
public sealed class ServiceClass
{
public String ServiceCall(ArgumentClass argument) => $"ServiceClass::ServiceCall({argument.AnyCall()})";
}
}
namespace MyCode
{
// Composite pattern, basically I need: "is a ArgumentClass"
// Obviously doesn't work - can't extend from sealed class
public class MyArgumentClass : IDontHaveControlOverThis.ArgumentClass
{
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
}
}
public class Program
{
public static void Main()
{
// I don't have control over this
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
//This obviously works
IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(arg)}");
// How to make this work?
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
}
}
编译器错误信息
Cannot implicitly convert type 'MyCode.MyArgumentClass' to 'IDontHaveControlOverThis.ArgumentClass'
注:强调我的
应该会提示您可以做什么
public class MyArgumentClass {
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
public static implicit operator IDontHaveControlOverThis.ArgumentClass(MyArgumentClass source) {
return source.arg;
}
}
现在您的 "wrapper" 根据需要公开了第三方依赖项
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
或直接
var myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
参考User-defined conversion operators (C# reference)
这可以允许抽象您的代码
namespace MyCode {
public interface IMyService {
String ServiceCall(MyArgumentClass argument);
}
public class MyServiceClass : IMyService {
public string ServiceCall(MyArgumentClass argument) {
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
return service.ServiceCall(argument);
}
}
}
根据您显示的代码示例,答案是您不能。您需要能够通过设置 属性 或创建具有不同构造函数参数的新实例来修改 IDontHaveControlOverThis.ArgumentClass
的行为,以便修改服务调用。 (现在总是 returns 相同的字符串,所以服务调用总是相同的)
如果您能够通过设置属性来修改 ArgumentClass 的行为。
您可以在自己的代码中为密封的 类 创建包装器,并在整个代码库中使用它。
public class MyArgumentClass
{
// TODO: Set this to a useful value of ArgumentClass.
internal IDontHaveControlOverThis.ArgumentClass InnerArgumentClass { get; }
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public virtual string ServiceCall(MyArgumentClass argument)
{
return innerServiceClass.ServiceCall(argument.InnerArgumentClass);
}
}
或
public class MyArgumentClass
{
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public string ServiceCall(MyArgumentClass argument)
{
var serviceArgument = Convert(argument);
return innerServiceClass.ServiceCall(serviceArgument);
}
private IDontHaveControlOverThis.ArgumentClass Convert(MyArgumentClass argument)
{
// TODO: implement.
}
}
假设我正在使用一个我无法控制 的库。该库公开了需要某些 class 参数的服务。 Class 标记为密封并且没有接口。
tl;dr:如何将 sealed class 重新实现为 interface?
代码示例:
using System;
namespace IDontHaveControlOverThis
{
// Note no interface and the class is being sealed
public sealed class ArgumentClass
{
public String AnyCall() => "ArgumentClass::AnyCall";
}
public sealed class ServiceClass
{
public String ServiceCall(ArgumentClass argument) => $"ServiceClass::ServiceCall({argument.AnyCall()})";
}
}
namespace MyCode
{
// Composite pattern, basically I need: "is a ArgumentClass"
// Obviously doesn't work - can't extend from sealed class
public class MyArgumentClass : IDontHaveControlOverThis.ArgumentClass
{
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
}
}
public class Program
{
public static void Main()
{
// I don't have control over this
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
//This obviously works
IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(arg)}");
// How to make this work?
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
}
}
编译器错误信息
Cannot implicitly convert type 'MyCode.MyArgumentClass' to 'IDontHaveControlOverThis.ArgumentClass'
注:强调我的
应该会提示您可以做什么
public class MyArgumentClass {
private IDontHaveControlOverThis.ArgumentClass arg = new IDontHaveControlOverThis.ArgumentClass();
public String AnyCall() => $"MyArgumentCLass::AnyCall({arg.AnyCall()})";
public static implicit operator IDontHaveControlOverThis.ArgumentClass(MyArgumentClass source) {
return source.arg;
}
}
现在您的 "wrapper" 根据需要公开了第三方依赖项
IDontHaveControlOverThis.ArgumentClass myArg = new MyCode.MyArgumentClass();
或直接
var myArg = new MyCode.MyArgumentClass();
Console.WriteLine($"Result: {service.ServiceCall(myArg)}");
参考User-defined conversion operators (C# reference)
这可以允许抽象您的代码
namespace MyCode {
public interface IMyService {
String ServiceCall(MyArgumentClass argument);
}
public class MyServiceClass : IMyService {
public string ServiceCall(MyArgumentClass argument) {
IDontHaveControlOverThis.ServiceClass service = new IDontHaveControlOverThis.ServiceClass();
return service.ServiceCall(argument);
}
}
}
根据您显示的代码示例,答案是您不能。您需要能够通过设置 属性 或创建具有不同构造函数参数的新实例来修改 IDontHaveControlOverThis.ArgumentClass
的行为,以便修改服务调用。 (现在总是 returns 相同的字符串,所以服务调用总是相同的)
如果您能够通过设置属性来修改 ArgumentClass 的行为。 您可以在自己的代码中为密封的 类 创建包装器,并在整个代码库中使用它。
public class MyArgumentClass
{
// TODO: Set this to a useful value of ArgumentClass.
internal IDontHaveControlOverThis.ArgumentClass InnerArgumentClass { get; }
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public virtual string ServiceCall(MyArgumentClass argument)
{
return innerServiceClass.ServiceCall(argument.InnerArgumentClass);
}
}
或
public class MyArgumentClass
{
public virtual string AnyCall() => "???";
}
public class MyServiceClass
{
private IDontHaveControlOverThis.ServiceClass innerServiceClass
= new IDontHaveControlOverThis.ServiceClass();
public string ServiceCall(MyArgumentClass argument)
{
var serviceArgument = Convert(argument);
return innerServiceClass.ServiceCall(serviceArgument);
}
private IDontHaveControlOverThis.ArgumentClass Convert(MyArgumentClass argument)
{
// TODO: implement.
}
}