在不公开暴露的情况下实现接口 属性 setter?
Implement interface without publicly exposing property setter?
下面的示例代码更好地解释了我的问题,它在评论中有很好的解释。 Browser 包含 BrowserModule 的各种实现者,这些模块具有对所有者浏览器的引用,因此它们可以在内部协同工作。每个 "main" 模块内部都有一个 SpecificModule,这些 "main" 模块仅作为将实现目标浏览器自动化库(如 Selenium 或 Puppeteer 等)的特定模块的代理...
对于 BrowserMouse 模块,例如,它将 SpecificModule 实例化为 BrowserPuppeteerChromiumSpecificMouse 并将调用委托给该特定模块。
我不希望 浏览器 class 的用户能够访问每个自动化库的底层实现,所以我以这种方式隐藏它们,但我有例如,鼠标位置的问题可以通过 Browser.Mouse.PositionOnDocument = new Point(123,123) 来设置,我希望这种能力只能在内部(命名空间级别)使用,而不是给图书馆的最终用户。
应该允许的是,例如,另一个模块想要更改鼠标的位置,它可以正常更改它只需访问 Browser.Mouse.PositionOnDocument = new Point();
但这应该只允许从同一个命名空间使用,而不是从外部使用,外部使用应该是只读的。
using System;
using System.Drawing;
namespace Test
{
using InternalNamespace;
class Program
{
public static void Main()
{
Browser browser = new Browser();
browser.Initialize();
Console.WriteLine(browser.Mouse.PositionOnDocument);
// This should NOT be allowed because its being used from outside the Browser namespace
browser.Mouse.PositionOnDocument = new Point(2, 2);
Console.WriteLine(browser.Mouse.PositionOnDocument);
}
}
}
namespace InternalNamespace
{
public class Browser
{
public BrowserMouse Mouse;
public void Initialize()
{
// Instantiate the main mouse with underlying specific puppeteer module
// pass a null module to the specific module because it doesnt need
// one underlying specific module
Mouse = new BrowserMouse(this, new BrowserPuppeteerChromiumSpecificMouse(this, null));
// This is OK because its inside the namespace
Mouse.PositionOnDocument = new Point(1, 1);
}
}
public class BrowserModule
{
// ExternalBrowser that owns this module.
protected Browser Browser { get; set; }
// Underlying specific module => Puppeteer, Selenium or anything.
protected BrowserModule SpecificModule { get; set; }
public BrowserModule(Browser browser, BrowserModule specificModule)
{
Browser = browser;
if (specificModule != null)
SpecificModule = specificModule;
}
}
interface ILocatable
{
Point PositionOnDocument { get; set; }
}
// This whole class just servers as a proxy for the underlying module.
public class BrowserMouse : BrowserModule, ILocatable
{
public BrowserMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
// This delegates the call to the specific module.
public Point PositionOnDocument
{
get
{
return ((ILocatable)SpecificModule).PositionOnDocument;
}
set
{
((ILocatable)SpecificModule).PositionOnDocument = value;
}
}
}
// The specific module implementation that is gonna be called from the main module.
public class BrowserPuppeteerChromiumSpecificMouse : BrowserModule, ILocatable
{
public BrowserPuppeteerChromiumSpecificMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
public Point PositionOnDocument { get; set; }
}
}
This interface allows implementers to expose a PositionOnDocument
不完全是,接口 需要 实施者履行合同合同是 属性 PositionOnDocument
和 public getter 和 setter。如果你不能改变接口,你就不能在没有 setter 的情况下实现它——你不能违约。
如果可以更改界面,请从合同中删除 setter。
interface IExternalBrowserLocatable
{
Point PositionOnDocument { get; }
}
如果您希望 setter 可在内部访问,请使其可见性 internal
。
internal class ExternalBrowserPuppeteerChromiumSpecificMouse : ExternalBrowserPuppeteerChromiumSpecificModule, IExternalBrowserLocatable
{
...
public Point PositionOnDocument { get; internal set; }
}
编辑:
如果您想要一个仅在内部可用的 setters 的通用接口,您可以这样做:
// You can probably come up with a better name.
internal interface IExternalBrowserLocatableMutable : IExternalBrowserLocatable
{
new Point PositionOnDocument { get; set; }
}
internal class ExternalBrowserPuppeteerChromiumSpecificMouse : ExternalBrowserPuppeteerChromiumSpecificModule, IExternalBrowserLocatableMutable
{
...
public Point PositionOnDocument { get; set; }
}
public
class 实现 internal
接口是完全合法的(而且非常有用!),您可以将实现 class 强制转换为直接 internal
接口或其 public
基础。通过 internal
接口(或 class 本身)访问时,您会同时获得 getter 和 setter。 public
接口仅公开 getter.
首先,internal
访问不允许来自 程序集 外部的访问,而不是来自不同命名空间的访问。在同一个程序集中引用来自完全不同的命名空间的 internal
成员是完全合法的,即使在同一个命名空间中也引用来自不同程序集的成员是非法的。我假设您想在 .NET internal
意义上限制访问,因为您所描述的特定于命名空间的行为是不可能的。
您的 ILocatable
界面很肤浅。它只是为了让你可以在内部进行转换 (ILocatable)SpecificModule
,而这种转换可能是灾难性的,因为 BrowserModule
不一定实现 ILocatable
,所以它是可能的 InvalidCastException
,这在正确的代码中永远不会发生。
既然你将 BrowserModule
暴露给 public,并且你显然希望任何 BrowserModule
都具有 属性 PositionOnDocument
,请将其转进入 abstract
class 与 abstract
属性 与 internal
setter:
public abstract class BrowserModule
{
// ExternalBrowser that owns this module.
protected Browser Browser { get; set; }
// Underlying specific module => Puppeteer, Selenium or anything.
protected BrowserModule SpecificModule { get; set; }
public abstract Point PositionOnDocument { get; internal set; }
...
}
public class BrowserMouse : BrowserModule
{
...
// This delegates the call to the specific module.
public override Point PositionOnDocument
{
get
{
return SpecificModule.PositionOnDocument;
}
internal set
{
SpecificModule.PositionOnDocument = value;
}
}
}
// The specific module implementation that is gonna be called from the main module.
public class BrowserPuppeteerChromiumSpecificMouse : BrowserModule
{
...
public override Point PositionOnDocument { get; internal set; }
}
下面的示例代码更好地解释了我的问题,它在评论中有很好的解释。 Browser 包含 BrowserModule 的各种实现者,这些模块具有对所有者浏览器的引用,因此它们可以在内部协同工作。每个 "main" 模块内部都有一个 SpecificModule,这些 "main" 模块仅作为将实现目标浏览器自动化库(如 Selenium 或 Puppeteer 等)的特定模块的代理...
对于 BrowserMouse 模块,例如,它将 SpecificModule 实例化为 BrowserPuppeteerChromiumSpecificMouse 并将调用委托给该特定模块。
我不希望 浏览器 class 的用户能够访问每个自动化库的底层实现,所以我以这种方式隐藏它们,但我有例如,鼠标位置的问题可以通过 Browser.Mouse.PositionOnDocument = new Point(123,123) 来设置,我希望这种能力只能在内部(命名空间级别)使用,而不是给图书馆的最终用户。
应该允许的是,例如,另一个模块想要更改鼠标的位置,它可以正常更改它只需访问 Browser.Mouse.PositionOnDocument = new Point();
但这应该只允许从同一个命名空间使用,而不是从外部使用,外部使用应该是只读的。
using System;
using System.Drawing;
namespace Test
{
using InternalNamespace;
class Program
{
public static void Main()
{
Browser browser = new Browser();
browser.Initialize();
Console.WriteLine(browser.Mouse.PositionOnDocument);
// This should NOT be allowed because its being used from outside the Browser namespace
browser.Mouse.PositionOnDocument = new Point(2, 2);
Console.WriteLine(browser.Mouse.PositionOnDocument);
}
}
}
namespace InternalNamespace
{
public class Browser
{
public BrowserMouse Mouse;
public void Initialize()
{
// Instantiate the main mouse with underlying specific puppeteer module
// pass a null module to the specific module because it doesnt need
// one underlying specific module
Mouse = new BrowserMouse(this, new BrowserPuppeteerChromiumSpecificMouse(this, null));
// This is OK because its inside the namespace
Mouse.PositionOnDocument = new Point(1, 1);
}
}
public class BrowserModule
{
// ExternalBrowser that owns this module.
protected Browser Browser { get; set; }
// Underlying specific module => Puppeteer, Selenium or anything.
protected BrowserModule SpecificModule { get; set; }
public BrowserModule(Browser browser, BrowserModule specificModule)
{
Browser = browser;
if (specificModule != null)
SpecificModule = specificModule;
}
}
interface ILocatable
{
Point PositionOnDocument { get; set; }
}
// This whole class just servers as a proxy for the underlying module.
public class BrowserMouse : BrowserModule, ILocatable
{
public BrowserMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
// This delegates the call to the specific module.
public Point PositionOnDocument
{
get
{
return ((ILocatable)SpecificModule).PositionOnDocument;
}
set
{
((ILocatable)SpecificModule).PositionOnDocument = value;
}
}
}
// The specific module implementation that is gonna be called from the main module.
public class BrowserPuppeteerChromiumSpecificMouse : BrowserModule, ILocatable
{
public BrowserPuppeteerChromiumSpecificMouse(Browser browser, BrowserModule specificModule) : base(browser, specificModule)
{
}
public Point PositionOnDocument { get; set; }
}
}
This interface allows implementers to expose a
PositionOnDocument
不完全是,接口 需要 实施者履行合同合同是 属性 PositionOnDocument
和 public getter 和 setter。如果你不能改变接口,你就不能在没有 setter 的情况下实现它——你不能违约。
如果可以更改界面,请从合同中删除 setter。
interface IExternalBrowserLocatable
{
Point PositionOnDocument { get; }
}
如果您希望 setter 可在内部访问,请使其可见性 internal
。
internal class ExternalBrowserPuppeteerChromiumSpecificMouse : ExternalBrowserPuppeteerChromiumSpecificModule, IExternalBrowserLocatable
{
...
public Point PositionOnDocument { get; internal set; }
}
编辑:
如果您想要一个仅在内部可用的 setters 的通用接口,您可以这样做:
// You can probably come up with a better name.
internal interface IExternalBrowserLocatableMutable : IExternalBrowserLocatable
{
new Point PositionOnDocument { get; set; }
}
internal class ExternalBrowserPuppeteerChromiumSpecificMouse : ExternalBrowserPuppeteerChromiumSpecificModule, IExternalBrowserLocatableMutable
{
...
public Point PositionOnDocument { get; set; }
}
public
class 实现 internal
接口是完全合法的(而且非常有用!),您可以将实现 class 强制转换为直接 internal
接口或其 public
基础。通过 internal
接口(或 class 本身)访问时,您会同时获得 getter 和 setter。 public
接口仅公开 getter.
首先,internal
访问不允许来自 程序集 外部的访问,而不是来自不同命名空间的访问。在同一个程序集中引用来自完全不同的命名空间的 internal
成员是完全合法的,即使在同一个命名空间中也引用来自不同程序集的成员是非法的。我假设您想在 .NET internal
意义上限制访问,因为您所描述的特定于命名空间的行为是不可能的。
您的 ILocatable
界面很肤浅。它只是为了让你可以在内部进行转换 (ILocatable)SpecificModule
,而这种转换可能是灾难性的,因为 BrowserModule
不一定实现 ILocatable
,所以它是可能的 InvalidCastException
,这在正确的代码中永远不会发生。
既然你将 BrowserModule
暴露给 public,并且你显然希望任何 BrowserModule
都具有 属性 PositionOnDocument
,请将其转进入 abstract
class 与 abstract
属性 与 internal
setter:
public abstract class BrowserModule
{
// ExternalBrowser that owns this module.
protected Browser Browser { get; set; }
// Underlying specific module => Puppeteer, Selenium or anything.
protected BrowserModule SpecificModule { get; set; }
public abstract Point PositionOnDocument { get; internal set; }
...
}
public class BrowserMouse : BrowserModule
{
...
// This delegates the call to the specific module.
public override Point PositionOnDocument
{
get
{
return SpecificModule.PositionOnDocument;
}
internal set
{
SpecificModule.PositionOnDocument = value;
}
}
}
// The specific module implementation that is gonna be called from the main module.
public class BrowserPuppeteerChromiumSpecificMouse : BrowserModule
{
...
public override Point PositionOnDocument { get; internal set; }
}