限制访问 class 的接口的作用

Role of interfaces for restricting access to a class

两个 classes,S 和 R 以及一条消息 M(作为 class、委托或其他实现)。

能否借助C#中的接口满足以下需求?如果是,如何?

  1. S 的任何对象都应能够将 M 发送到 R 的任何对象
  2. 任何其他 class X 的任何对象 不能 能够将 M 发送到 R
  3. 的任何对象
  4. S 的对象应不能 能够将任何其他消息 N 发送到 R 的对象(尽管 R 可能会从其他 classes 的对象接收其他消息类型)
  5. 任何违反上述规定发送 M 或 N 的尝试都将导致编译器错误(即应进行静态检查)

这对我来说听起来很简单和自然,但无论我如何努力在 SO 或 web 上搜索这里,我都找不到任何有用的东西。我发现的唯一一件事是对工厂模式的引用,我认为它在这里并不适用,因为问题不在于 S、R 或 X 的构造。

当然,如果有任何其他不涉及接口的解决方案,我也将不胜感激。

BTW:虽然听起来有点像,但这既不是家庭作业,也不是来自专业背景。我只是一个业余程序员,试图探索我喜欢的语言的可能性。

编辑:

为了提供一个(假设的)代码示例:对我来说,理想的方式是能够编写如下(伪)代码。 我知道语言不支持那个。这就是为什么我要求找到 模式 或实现相同目标的东西的原因。

class Receiver
{
    permits[MSender] void MessageM(); // <- I know that the "permits[]" access modifier does not exist in C#!!!
    permits[NSender] void MessageN();
}

class MSender
{
    Receiver r;
    public void JustDoIt()
    {
        r.MessageM(); // compiles successfully
        r.MessageN(); // does not compile
    }
}

class NSender // totally unrelated to sender despite the similar name
{
    Receiver r;
    public void DoItDifferently()
    {
        r.MessageM(); // does not compile
        r.MessageN(); // compiles successfully
    }
}

好吧,我已经对此发表了评论,不确定这是否是您要查找的内容,但您可以使用泛型来限制类型参数。这会检查编译时间。

public interface IOtherInterface
{

}

public interface IAnInterface<T> where T : IOtherInterface
{
    void DoSomething(T parameter);
}

public class ThisWontWork
{

}

var other = new OtherInterface();

var an = new AnInterface();

an.DoSomething(other); // this works

var wontWork = new ThisWontWork();

an.DoSomething(wontWork);  // will not build

省略了接口的 类,但我猜你明白了。

在此处阅读更多内容:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

根据我的评论,包装您的 non-generic 类 S 和 R,然后将类型约束应用于包装的 类。

更新:工作代码。实际上不需要泛型或包装 类 。仅实现接口

using System;

public interface IM
{
    void SendM(IRM target, string message);
}

public interface IN
{
    void SendN(IRN target, string message);
}

public interface IRM
{
    string ReceiveM(IM source, string message);
}

public interface IRN
{
    string ReceiveN(IN source, string message);
}

public class S : IM
{
     public void SendM(IRM target, string message) => target.ReceiveM(this, message);
     // Argument 1 cannot convert from S to X :-
     public void SendN(IRN target, string message) => target.ReceiveN(this, message);     }

public class X : IN
{
    public void SendN(IRN target, string message) => target.ReceiveN(this, message);
    // Argument 1 cannot convert from X to S :- 
    public void SendM(IRM target, string message) => target.ReceiveM(this, message);     }

public class Receiver : IRN, IRM
{
    public string ReceiveM(IM source, string message)
    {
        throw new NotImplementedException();
    }

    public string ReceiveN(IN source, string message)
    {
        throw new NotImplementedException();
    }
}

public class Program
{
    public static void Main()
    {
        var r = new Receiver();
        var s = new S();
        s.SendM(r, "message via M");
        var x = new X();
        x.SendN(r, "message via N");
       // s.SendN(r,"does not compile nor in intellisense");
    }
}

访客模式概述:

https://dotnetfiddle.net/MfGWqw

public class Program
{
    public static void Main()
    {
        R recv = new R();
        new S().send( recv, new M()); // OK
        new S().send( recv, new N()); // Compilation error (line 9, col 3): 
                                      // The best overloaded method match for 'S.send(R, M)' 
                                      // has some invalid arguments
        new X().send( recv, new N()); // OK
        new X().send( recv, new M()); // Also compilation error ... 
    }


}

// Message types    

public class M{}

public class N{}

// Receiver     
public class R
{
    public void accept( S sender, M message){}
    public void accept( X sender, N message){}
}

// Sender types    

public class S
{
    public void send( R receiver, M message )
    {
        receiver.accept(this, message);
    }
}

public class X
{
    public void send( R receiver, N message )
    {
        receiver.accept(this, message);
    }
}

我在那个例子中没有使用接口,当然,你可以。我只是想概述满足您要求的模式。我希望您需要根据自己的需要对其进行调整。

编辑:回答您的评论... 如果您害怕 S 中的恶意实现,您可以使用 explicit interface implementations 来解决这个问题。一个例子:

public interface IMReceiver
{
    void accept( S sender, M message);
}

然后更改 R :

public class R : IMReceiver
{
    void IMReceiver.accept( S sender, M message){} // <= explicit interface implementation.
                                                   // only visible when the reference is of 
                                                   // that interface type.
    public void accept( X sender, N message){} // you would do the same for N ...
}

S

public class S
{
    public void send( IMReceiver receiver, M message )
    {
        // receiver now has only accept( S, M ) available.
        receiver.accept(this, message);
        // MALICIOUS vv
        receiver.accept(new X(), new N()); // compilation error
    }
}

这里我只为 S 和 M 做了这个例子,但你可能想为 X 和 N 做同样的事情。

https://dotnetfiddle.net/b14BOc

由 Fildor 提供(已接受) - 这基本上是 访问者模式加上通过显式接口实现对接收方的额外访问限制 - 我也有准备了一个完整的示例来说明它。

滥用(即来自被禁止的发件人的发送消息)新创建的接收器的唯一方法是通过显式转换为与创建相应虚拟发件人对象相关的接口之一。作为一种安全风险,我认为这是可以接受的,因为它很难被意外绕过。

public interface IMReceiver
{
    void MessageM(MSender sender);
}

public interface INReceiver
{
    void MessageN(NSender sender);
}

public class Receiver: IMReceiver, INReceiver
{
    string name;
    public Receiver(string newName) {name = newName;}

    void IMReceiver.MessageM(MSender sender) {Console.WriteLine(name+" received Message M from "+sender.Name);}
    void INReceiver.MessageN(NSender sender) {Console.WriteLine(name+" received Message N from "+sender.Name);}
}

public class MSender
{
    void sendMessageMTo(IMReceiver r) {r.MessageM(this);}

    public readonly string Name = "an MSender";
    Receiver r1 = new Receiver("Alice");
    Receiver r2 = new Receiver("Bob");

    public void JustDoIt()
    {
        sendMessageMTo(r1);
        sendMessageMTo(r2);

        // thinkable abuses:

        // sendMessageNTo(r1); // is simply not defined
        // r1.MessageN(this); // does not compile without cast
        // (r1 as INReceiver).MessageN(this); // does not compile with this Sender type
        (r1 as INReceiver).MessageN(new NSender()); // possible, but unlikely to happen by accident
    }
}

public class NSender
{
    void sendMessageNTo(INReceiver r) {r.MessageN(this);}

    public readonly string Name = "an NSender";
    Receiver r3 = new Receiver("Clive");
    Receiver r4 = new Receiver("Dan");

    public void DoItDifferently()
    {
        sendMessageNTo(r3);
        sendMessageNTo(r4);
    }
}

在主要部分:

MSender ms = new MSender();
NSender ns = new NSender();

ms.JustDoIt();
ns.DoItDifferently();