我试图理解接口,以便我可以在我的程序中实现它们,但我无法想象我应该如何使用它们。也给我一些在 C# 中使用多重继承的例子
9 回答
接口的一个很好的例子是存储库模式。您的接口将定义 Get、GetAll、Update、Delete 等方法。没有实现,只是函数签名。
然后,您可以编写该类的“具体”实现以使用 MySQL。不过,您的 UI 应该只引用界面。
稍后,如果您决定更改为 Microsoft SQL,您将编写另一个具体的实现,但您的 UI 代码不必(太多)更改。
C# 中不存在多重继承,因为您只能从一个“具体”类继承;尽管您可以继承(或“实现”)任意数量的接口。
我正在写一个视频游戏。在这个视频游戏中,我对游戏中的对象施加不同的力。推力、冲击力、重力。虽然它们的计算方式不同,但它们都具有相同的基本元素。我需要调用一个更新函数来评估力并将力添加到它所连接的对象上。
所以,我所做的是创建一个 IForce 接口,它的签名具有更新功能。我所有的部队都实现了这个接口:
public interface IForce
{
void Update(Particle particle, GameTime gameTime);
}
这是一个示例实现。
public class Spring : IForce
{
private Particle ThisParticle;
private Particle ThatParticle;
private float K;
public Spring(Particle thisParticle, Particle thatParticle, float k)
{
ThisParticle = thisParticle;
ThatParticle = thatParticle;
}
public void Update(Particle particle, GameTime gameTime)
{
float X = Vector3.Length(ThisParticle - ThatParticle);
ThisParticle.Forces.Add(K * X);
}
}
更新功能具有简化的弹簧力更新,使其更易于理解。
这在几个方面有所帮助。
我可以完全改变力的计算方式,而不会影响我代码的其他部分。我一直这样做。同样,我很容易添加新的力量。只要它实现了 IForce 接口,我就知道它会与我现有的代码很好地结合。
它帮助的另一种方法是处理大量的力。我有一个包含 IForce 列表的强制注册表。由于所有部队都实现了该接口并具有更新功能,因此在我的游戏中更新所有部队非常容易。当我创建力时,我将其添加到列表中。然后,我遍历列表并调用每个元素更新函数,而不用担心它是什么类型的力以及我所有的力更新。
我每天都在很多不同的情况下使用接口。他们太棒了!
注意:接口用于不惜一切代价限制和访问来自不同类的方法或事件等,这意味着我们可以在任何类中定义更多方法,但是当我们通过接口调用方法时,意味着我们只需要受限方法之外的方法。在下面的程序中,用户 1 可以同时使用读取和写入,但用户 2 可以写入和执行。请参阅下面的这个程序............
namespace ExplConsole
{
class Program
{
static void Main ()
{
System.Console.WriteLine("Permission for User1");
User1 usr1 = new Test(); // Create instance.
usr1.Read(); // Call method on interface.
usr1.Write();
System.Console.WriteLine("Permission for User2");
User2 usr2 = new Test();
usr2.Write();
usr2.Execute();
System.Console.ReadKey();
}
}
interface User1
{
void Read();
void Write();
}
interface User2
{
void Write();
void Execute();
}
class Test : NewTest,User1, User2
{
public void Read()
{
Console.WriteLine("Read");
}
public void Write()
{
Console.WriteLine("Write");
}
}
class NewTest
{
public void Execute()
{
Console.WriteLine("Execute");
}
}
}
输出:
Permission for User1
Read
Write
Permission for User2
Write
Execute
接口只是为您的对象定义公共元素(例如属性、方法、事件)的契约,而不是行为。
interface IDog
{
void WagTail(); //notice no implementation
ISound Speak(); //notice no implementation
}
class Spaniel : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my long, hairy tail");
}
public ISound Speak()
{
return new BarkSound("yip");
}
}
class Terrier : IDog
{
public void WagTail()
{
Console.WriteLine("Shook my short tail");
}
public ISound Speak()
{
return new BarkSound("woof");
}
}
更新
在“真实示例”中,我使用以下接口: - 单元测试 - GENERICS(例如存储库、网关、设置)
interface Repository<T>{
T Find(Predicate<T>);
List<T> ListAll();
}
interface Gateway<T>{
T GetFrom(IQuery query);
void AddToDatabase(IEntity entityItem);
}
interface Settings<T>{
string Name { get; set; }
T Value { get; set; }
T Default { get; }
}
有时过于抽象只会妨碍您,参考实现细节实际上可以澄清事情。因此,我将提供接近金属的接口解释,使我最终了解它们。
接口只是声明一个类实现了一些虚函数以及这些虚函数应该如何在类的vtable中布局的一种方式。当你声明一个接口时,你实际上是在给编译器一个虚函数表的高级描述。当你实现一个接口时,你是在告诉编译器你想在你的类中包含那个接口引用的虚表。
接口的目的是您可以将实现接口 I 的类隐式转换为接口 I 的实例:
interface I {
void doStuff();
}
class Foo : I {
void doStuff() {}
void useAnI(I i) {}
}
var foo = new Foo();
I i = foo; // i is now a reference to the vtable pointer for I in foo.
foo.useAnI(i); // Works. You've passed useAnI a Foo, which can be used as an I.
这是一个(在Java中,但这并不重要,因为它们很相似): 在我的项目中,我创建了简单的界面:
public interface Identifiable<T> {
public T getId();
}
这是对某些注释的简单替换。下一步:我已经让所有实体类都实现了这个接口。
第三步,编写一些类似语法糖的方法:
public <T> List<T> ids(List<? extends Identifiable<T> entities) { ... }
这只是一个例子。
更复杂的示例类似于验证规则:您有一些验证引擎(可能由您编写)和一个简单的规则接口:
public interface ValidationRule {
public boolean isValid(...);
}
因此,此引擎需要您执行规则。当然会有多重继承,因为你肯定希望不止一条规则。
多重继承是关于让一个类在多种情况下可用:[伪代码]
interface Shape {
// shape methods like draw, move, getboundingrect, whatever.
}
interface Serializable {
// methods like read and write
}
class Circle : public Shape, public Serializable {
// TODO: implement Shape methods
// TODO: implement Serializable methods
}
// somewhere later
{
Circle circle;
// ...
deserializer.deserialize(circle);
// ...
graphicsurface.draw(circle);
// ...
serializer.serialize(circle);
}
这个想法是你的 Circle 类实现了两个不同的接口,它们在非常不同的情况下使用。
在我看来,一个简单的答案,而且我自己对接口有点陌生,在一个类中实现一个接口本质上意味着:“这个类必须在接口中定义函数(和参数)”。
由此可见,只要某个类实现了接口,您就可以确定您能够调用这些函数。
如果多个不同的类实现相同的接口,您可以将它们全部“强制转换”到接口并调用它们上的所有接口函数,这可能会产生不同的效果,因为每个类可能有不同的函数实现。
例如,我一直在创建一个程序,它允许用户生成 4 种不同类型的地图。为此,我创建了 4 种不同类型的生成器类。它们都实现了“IGenerator”接口:
public interface IGenerator {
public void generateNow(int period);
}
这告诉他们至少定义一个“public generateNow(int period)”函数。
无论我最初拥有什么生成器,在将其转换为“IGenerator”之后,我都可以在其上调用“generateNow(4)”。我不必确定我返回的是什么类型的生成器,这基本上意味着不再有“变量 instanceof Class1”、“variable instanceof Class2”等在巨大的 if 语句中。
看一下您熟悉的东西 - 即 C# 中的 List 集合。列表定义 IList 接口,通用列表定义 IList 接口。IList 公开了 Add、Remove 等函数,List 实现了这些函数。还有一些 BindingLists 以稍微不同的方式实现 IList。
我也会推荐Head First Design Patterns。代码示例使用 Java,但很容易翻译成 C#,此外,它们还将向您介绍接口和设计模式的真正力量。