C#无铸造的多形设计
我有一个生成器类,它可以生成具有不同接口的对象,这些接口的概率不同。生成器生成的所有对象的类型均为C#无铸造的多形设计,c#,polymorphism,C#,Polymorphism,我有一个生成器类,它可以生成具有不同接口的对象,这些接口的概率不同。生成器生成的所有对象的类型均为基类基类是一个抽象基类 假设接口是I1,I2 我有另一个类Resolver,它有两个接口的多态方法,如下所示: Resolve(I1 myObj){//code for I1} Resolve(I2 myObj){//code for I2} 主类如下所示: 基类事件=Generator.generate()//事件是实现I1或I2。在运行时之前不知道什么接口。 解析程序。解析(事件)//这里我得
基类
<代码>基类是一个抽象基类
假设接口是I1
,I2
我有另一个类Resolver
,它有两个接口的多态方法,如下所示:
Resolve(I1 myObj){//code for I1}
Resolve(I2 myObj){//code for I2}
主类如下所示:
基类事件=Generator.generate()//事件是实现I1或I2。在运行时之前不知道什么接口。
解析程序。解析(事件)//这里我得到了一个错误,因为事件是基类类型,而不是I1或I2类型。
是否有一种方法可以解决此问题,而无需显式检查接口类型并将其转换为适当的接口。我来自python背景,所以静态类型语言对我来说是新的。EDIT我调整了我的答案,因为我第一次没有正确理解这个问题
我认为,如果不使用强制类型转换,就无法实现您想要的目标。据我所知,只要您通过基本类型引用从Generator.generate()
获得的对象,就不可能在不强制转换的情况下再次通过其专用类型访问该对象
我可以想出两种对你有意思的选择。一种是使用C#7模式匹配(有点像使用强制转换),另一种是使用动态
模式匹配
using System;
namespace EventREsolver
{
public interface IEvent { }
public class Event1 : IEvent { }
public class Event2 : IEvent { }
public class Resolver
{
public void Resolve(IEvent theEvent)
{
switch (theEvent)
{
case Event1 e1: Resolve(e1); break;
case Event2 e2: Resolve(e2); break;
default: throw new ArgumentException("not a recognized type", nameof(theEvent));
}
}
private void Resolve(Event1 theEvent)
{
Console.WriteLine("Resolve I1");
}
private void Resolve(Event2 theEvent)
{
Console.WriteLine("Resolve I2");
}
}
public class Generator
{
int state = 0;
public IEvent Generate()
{
if (state == 0)
{
state++;
return new Event1();
}
return new Event2();
}
}
class Program
{
static void Main(string[] args)
{
var generator = new Generator();
var event1 = generator.Generate();
var event2 = generator.Generate();
var resolver = new Resolver();
resolver.Resolve(event1);
resolver.Resolve(event2);
Console.ReadKey();
}
}
}
动态
using System;
namespace EventREsolver
{
public interface IEvent { }
public class Event1 : IEvent { }
public class Event2 : IEvent { }
public class Resolver
{
public void Resolve(Event1 theEvent)
{
Console.WriteLine("Resolve I1");
}
public void Resolve(Event2 theEvent)
{
Console.WriteLine("Resolve I2");
}
}
public class Generator
{
int state = 0;
public IEvent Generate()
{
if (state == 0)
{
state++;
return new Event1();
}
return new Event2();
}
}
class Program
{
static void Main(string[] args)
{
var generator = new Generator();
dynamic event1 = generator.Generate();
dynamic event2 = generator.Generate();
var resolver = new Resolver();
resolver.Resolve(event1);
resolver.Resolve(event2);
Console.ReadKey();
}
}
}
编辑我调整了答案,因为我第一次没有正确理解问题
我认为,如果不使用强制类型转换,就无法实现您想要的目标。据我所知,只要您通过基本类型引用从Generator.generate()
获得的对象,就不可能在不强制转换的情况下再次通过其专用类型访问该对象
我可以想出两个你可能感兴趣的选择。一种是使用C#7模式匹配(有点像使用强制转换),另一种是使用动态
模式匹配
using System;
namespace EventREsolver
{
public interface IEvent { }
public class Event1 : IEvent { }
public class Event2 : IEvent { }
public class Resolver
{
public void Resolve(IEvent theEvent)
{
switch (theEvent)
{
case Event1 e1: Resolve(e1); break;
case Event2 e2: Resolve(e2); break;
default: throw new ArgumentException("not a recognized type", nameof(theEvent));
}
}
private void Resolve(Event1 theEvent)
{
Console.WriteLine("Resolve I1");
}
private void Resolve(Event2 theEvent)
{
Console.WriteLine("Resolve I2");
}
}
public class Generator
{
int state = 0;
public IEvent Generate()
{
if (state == 0)
{
state++;
return new Event1();
}
return new Event2();
}
}
class Program
{
static void Main(string[] args)
{
var generator = new Generator();
var event1 = generator.Generate();
var event2 = generator.Generate();
var resolver = new Resolver();
resolver.Resolve(event1);
resolver.Resolve(event2);
Console.ReadKey();
}
}
}
动态
using System;
namespace EventREsolver
{
public interface IEvent { }
public class Event1 : IEvent { }
public class Event2 : IEvent { }
public class Resolver
{
public void Resolve(Event1 theEvent)
{
Console.WriteLine("Resolve I1");
}
public void Resolve(Event2 theEvent)
{
Console.WriteLine("Resolve I2");
}
}
public class Generator
{
int state = 0;
public IEvent Generate()
{
if (state == 0)
{
state++;
return new Event1();
}
return new Event2();
}
}
class Program
{
static void Main(string[] args)
{
var generator = new Generator();
dynamic event1 = generator.Generate();
dynamic event2 = generator.Generate();
var resolver = new Resolver();
resolver.Resolve(event1);
resolver.Resolve(event2);
Console.ReadKey();
}
}
}
考虑使用依赖项注入来允许事件对象调用解析器本身
public interface IResolvable
{
void Resolve(Resolver resolver);
}
public interface I1 : IResolvable { //... }
public interface I2 : IResolvable { //... }
public class Resolver
{
public void Resolve(I1 i) { //... }
public void Resolve(I2 i) { //... }
}
public abstract class BaseClass : IResolvable
{
public abstract void Resolve(Resolver resolver);
//...
}
实现将类似于:
public class Implementation1 : BaseClass, I1
{
public override void Resolver(Resolver resolver)
{
resolver.Resolve(this);
}
//...
}
然后称之为:
Resolver resolver = new Resolver();
IResolvable evnt = Generator.Generate();
evnt.Resolve(resolver);
我们可以更进一步,为解析器创建一个接口,这样我们就可以模拟它进行单元测试,并充分利用DI模式
public interface IResolver
{
void Resolve(I1 i) { //... }
void Resolve(I2 i) { //... }
}
然后我们改变了IResolvable的定义
public interface IResolvable
{
void Resolve(IResolver resolver);
}
考虑使用依赖项注入来允许事件对象调用解析器本身
public interface IResolvable
{
void Resolve(Resolver resolver);
}
public interface I1 : IResolvable { //... }
public interface I2 : IResolvable { //... }
public class Resolver
{
public void Resolve(I1 i) { //... }
public void Resolve(I2 i) { //... }
}
public abstract class BaseClass : IResolvable
{
public abstract void Resolve(Resolver resolver);
//...
}
实现将类似于:
public class Implementation1 : BaseClass, I1
{
public override void Resolver(Resolver resolver)
{
resolver.Resolve(this);
}
//...
}
然后称之为:
Resolver resolver = new Resolver();
IResolvable evnt = Generator.Generate();
evnt.Resolve(resolver);
我们可以更进一步,为解析器创建一个接口,这样我们就可以模拟它进行单元测试,并充分利用DI模式
public interface IResolver
{
void Resolve(I1 i) { //... }
void Resolve(I2 i) { //... }
}
然后我们改变了IResolvable的定义
public interface IResolvable
{
void Resolve(IResolver resolver);
}
下面的代码演示了不需要强制转换的虚拟函数方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
interface IBase
{
void Function();
}
class BaseClass : IBase
{
public virtual void Function()
{
}
}
interface I1: IBase
{
}
interface I2 : IBase
{
}
class C1: BaseClass, I1
{
public override void Function()
{
Console.WriteLine("Hello from C1");
}
}
class C2 : BaseClass, I1
{
public override void Function()
{
Console.WriteLine("Hello from C2 !!!");
}
}
static class Generator
{
public static BaseClass generateC1()
{
return new C1();
}
public static BaseClass generateC2()
{
return new C2();
}
}
class Program
{
static void Main(string[] args)
{
BaseClass b1 = Generator.generateC1();
b1.Function();
Console.WriteLine("-------");
BaseClass b2 = Generator.generateC2();
b2.Function();
Console.WriteLine("End!");
}
}
}
下面的代码演示了不需要强制转换的虚拟函数方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
interface IBase
{
void Function();
}
class BaseClass : IBase
{
public virtual void Function()
{
}
}
interface I1: IBase
{
}
interface I2 : IBase
{
}
class C1: BaseClass, I1
{
public override void Function()
{
Console.WriteLine("Hello from C1");
}
}
class C2 : BaseClass, I1
{
public override void Function()
{
Console.WriteLine("Hello from C2 !!!");
}
}
static class Generator
{
public static BaseClass generateC1()
{
return new C1();
}
public static BaseClass generateC2()
{
return new C2();
}
}
class Program
{
static void Main(string[] args)
{
BaseClass b1 = Generator.generateC1();
b1.Function();
Console.WriteLine("-------");
BaseClass b2 = Generator.generateC2();
b2.Function();
Console.WriteLine("End!");
}
}
}
是的,您可以有一个公共接口“I”,它用作基类的基。我将有一个虚拟函数,它将在分别从I1和I2派生的自定义类中被重写。例如,C1来自I1,C2来自I2。接下来,在生成器中生成C1或C2。然后不需要检查类型,只需调用虚拟函数。它将根据类的类型执行不同的代码。让我知道这是否有意义,这样我可以写一个更具说明性的答案。听起来很有趣。是的。很抱歉耽搁了,请检查我的答案。是的,您可以有一个公共接口“I”,它用作基类的基类。我将有一个虚拟函数,它将在分别从I1和I2派生的自定义类中被重写。例如,C1来自I1,C2来自I2。接下来,在生成器中生成C1或C2。然后不需要检查类型,只需调用虚拟函数。它将根据类的类型执行不同的代码。让我知道这是否有意义,这样我可以写一个更具说明性的答案。听起来很有趣。是的。很抱歉耽搁了,请检查我的答案。和我的问题很相似,但不一样。在我的例子中,解析器将有许多解析方法,每个I1,I2。。。接口。在不同的方法中有不同的代码。@Farseer:请看我调整后的答案。我不会这么快就求助于
dynamic
。任何涉及<代码>动态代码>的问题都必须在运行时完全解决,这会变得更慢,而且编译器无法保护您避免明显的错误。VuVirt的建议(实质上为基类
添加了一个虚拟的Resolve
方法)允许编译器验证该方法是否存在于所有涉及的类中,并且您不必进行任何类型检查。与我的问题非常类似,但不同。在我的例子中,解析器将有许多解析方法,每个I1,I2。。。接口。在不同的方法中有不同的代码。@farsear:请参阅我调整过的答案。我不会这么快地求助于dynamic
的。任何涉及<代码>动态代码>的问题都必须在运行时完全解决,这会变得更慢,而且编译器无法保护您避免明显的错误。VuVirt的建议(实质上为基类
添加了一个虚拟的Resolve
方法)允许编译器验证该方法是否存在于所有涉及的类中,并且您不必进行任何类型检查。很好,我愚蠢的问题是它是否应该使用而不是基类