用于限制对类

时间:2018-03-01 08:46:05

标签: c# interface separation-of-concerns visitor

两个类,S和R以及消息M(实现为类,委托或其他)。

我可以借助C#中的接口来满足以下要求吗?如果是,怎么做?

  1. S的任何对象都应该能够将M发送给R
  2. 的任何对象
  3. 任何其他类X的任何对象能够将M发送给R的任何对象
  4. S的对象能够向R的对象发送任何其他消息N(尽管R可能从其他类'对象接收其他消息类型)
  5. 任何违反上述规定发送M或N的企图都会导致编译错误(即应进行静态检查)
  6. 这对我来说听起来很简单和自然,但无论我如何努力在SO或网上搜索,我都找不到任何有用的东西。我发现的唯一一件事是对工厂模式的引用,我认为这并不适用于此,因为问题不在于构建S,R或X.

    当然,我也很感激任何其他不涉及接口的解决方案。

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

    修改

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

    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
        }
    }
    

4 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

从我的评论开始,包装非泛型类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");
    }
}

答案 2 :(得分:1)

访客模式大纲:

请参阅https://dotnetfiddle.net/MfGWqw

Function lastDay(year As String, month As String) As Date
    Dim ld As Date
    If LCase(month) = "all" Then month = "DEC"
    ld = DateValue("01 " & month & Space(1) & year)
    Dim d As Double
    d = WorksheetFunction.EoMonth(ld, 0)
    ld = CDate(d)
    lastDay = ld
End Function

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

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

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);
    }
}

然后更改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

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

请参阅https://dotnetfiddle.net/b14BOc

答案 3 :(得分:0)

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

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

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);
    }
}

并在Main:

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

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