C# 是一个没有成员的界面,它适合于表示“一个”;不透明把手“;给图书馆用户?

C# 是一个没有成员的界面,它适合于表示“一个”;不透明把手“;给图书馆用户?,c#,.net,interface,C#,.net,Interface,假设我有一个抽象对象,它可以由多个独立的插件作者实现。(例如,一个bug数据库连接)我不希望我的比特的消费者必须处理每一种特定的插件类型 我还想将解析配置文件的过程与实际初始化数据库插件和其他类似内容的过程分开 为此,我想出了这样的办法: public interface IConfiguration { // No members } public interface IConnection { // Members go in here void Create();

假设我有一个抽象对象,它可以由多个独立的插件作者实现。(例如,一个bug数据库连接)我不希望我的比特的消费者必须处理每一种特定的插件类型

我还想将解析配置文件的过程与实际初始化数据库插件和其他类似内容的过程分开

为此,我想出了这样的办法:

public interface IConfiguration
{
    // No members
}

public interface IConnection
{
    // Members go in here
    void Create();
    void Update();
    void Delete();
}

public interface IConnectionProvider
{
    // Try to interpret file as a configuration, otherwise return null
    IConfiguration ParseConfiguration(Stream configurationContents);
    IConnection Connect(IConfiguration settings);
}

public class ThingyRepository
{
    // Lets say there is a constructor that initializes this with something
    List<IConnectionProvider> providers;

    // Insulates people from the actual connection provider
    KeyValuePair<IConfiguration, IConnectionProvider> Parse(string filename)
    {
        IConnection result = null;
        IConnectionProvider resultProvider = null;
        foreach (var provider in this.providers)
        {
            using (Stream fs = OpenTheFileReadonly(filename))
            {
                IConnection curResult = provider.ParseConfiguration(fs);
                if (curResult == null)
                {
                    continue;
                }
                else
                {
                    if (result == null)
                    {
                        result = curResult;
                        resultProvider = provider;
                    }
                    else
                    {
                        throw new Exception ("ambguity!");
                    }
                }
            }
        }

        if (result == null)
        {
            throw new Exception ("can't parse!");
        }

        return new KeyValuePair<IConfiguration, IConnectionProvider>(
            result, resultProvider);
    }
}
公共接口i配置
{
//没有成员
}
公共接口i连接
{
//成员们到这里来
void Create();
无效更新();
作废删除();
}
公共接口IConnectionProvider
{
//尝试将文件解释为配置,否则返回null
i配置解析配置(流配置内容);
i连接连接(i配置设置);
}
公共类存储
{
//假设有一个构造函数用某种东西初始化它
名单提供者;
//将用户与实际的连接提供程序隔离
KeyValuePair解析(字符串文件名)
{
i连接结果=空;
IConnectionProvider resultProvider=null;
foreach(此.providers中的var提供程序)
{
使用(流fs=OpenTheFileReadonly(文件名))
{
IConnection curResult=provider.ParseConfiguration(fs);
如果(curResult==null)
{
继续;
}
其他的
{
如果(结果==null)
{
结果=当前结果;
resultProvider=提供者;
}
其他的
{
抛出新异常(“模糊!”);
}
}
}
}
如果(结果==null)
{
抛出新异常(“无法解析!”);
}
返回新的KeyValuePair(
结果,结果提供者);
}
}
我的问题是,我有一个空接口,它应该作为一个不透明的句柄来处理从指定文件加载的任何设置。IConnectionProvider的特定实现者知道它将从文件加载的配置中需要哪些位,但是这个库的用户应该与这些信息隔离


但是对于我来说,有一个空的界面似乎很奇怪。这类事情有意义吗?或者我犯了可怕的错误吗?

没有成员的接口的基本概念,它只是将实现者标识为某种东西,而不是接口的正常工作,即标识对象拥有或做什么,称为“标志接口”。它有它的用途,但要谨慎使用。一、 例如,通常以分层格式使用它们来标识应持久化到特定数据存储的域对象:

//no direct implementors; unfortunately an "abstract interface" is kind of redundant
//and there's no way to tell the compiler that a class inheriting from this base 
//interface is wrong,
public interface IDomainObject
{
   int Id {get;}
}

public interface IDatabaseDomainObject:IDomainObject { }

public interface ICloudDomainObject:IDomainObject { }

public class SomeDatabaseEntity:IDatabaseDomainObject
{
    public int Id{get;set;}

    ... //more properties/logic
}

public class SomeCloudEntity:ICloudDomainObject
{
    public int Id{get;set;}

    ... //more properties/logic
}
派生接口没有告诉我关于实现对象的结构的任何新信息,只是该对象属于该特定子域,允许我进一步控制可以在以下位置传递的内容:

//I can set up a basic Repository pattern handling any IDomainObject...
//(no direct concrete implementors, though I happen to have an abstract)
public interface IRepository<T> where T:IDomainObject
{
    public TDom Retrieve<TDom>(int id) where TDom:T;
}

//... Then create an interface specific to a sub-domain for implementations of
//a Repository for that specific persistence mechanism...
public interface IDatabaseRepository:IRepository<IDatabaseDomainObject>
{
    //... which will only accept objects of the sub-domain.
    public TDom Retrieve<TDom>(int id) where TDom:IDatabaseDomainObject;
}
//我可以设置一个基本的存储库模式来处理任何IDomain对象。。。
//(虽然我碰巧有一个摘要,但没有直接的具体实现者)
公共接口IRepository,其中T:IDomainObject
{
公共时差检索(int id),其中时差:T;
}
//... 然后创建特定于子域的接口,以实现
//特定持久性机制的存储库。。。
公共接口IDatabaseRepository:IRepository
{
//…只接受子域的对象。
公共TDom检索(int id),其中TDom:IDatabaseDomainObject;
}
可以在编译时检查生成的实现及其用法,以证明ICloudDomainObject没有被传递到IDatabaseRepository,并且任何时候都不能将字符串或字节[]传递到存储库进行存储。这种编译时安全性在属性或属性上是不可能的,属性或属性是将类“标记”为具有特殊意义的其他主要方法


因此,简而言之,这本身并不是一个糟糕的做法,但一定要问问自己想要从flag接口中得到什么,问问自己是否有任何通常在IConfiguration上实现的状态或逻辑数据(可能是所述配置的名称或其他标识符,或者将其加载或保存到所选数据存储的方法)可能需要一些强制性的标准化。

我认为这是完全正确的。我正在设计一个API,调用方必须首先获得一个不透明的“会话”对象,然后将其传递给后续调用


API的不同实现将使用完全不同的会话对象实现,因此会话对象显然不是具有不同子类的抽象类;这是一个界面。由于会话对象没有对调用方可见的行为,因此在我看来,唯一的逻辑模型是一个没有成员的接口。

这是一个旧的Java hack。在C#中,当您获得属性时,它没有意义。除了属性不能用作GTP之外,例如字典的键类型或列表的元素类型。我认为他们有自己的位置,如果使用得少并且有充分的理由的话。也许代码有味道,但这不是一个很好的练习。@Hans:在这里使用属性会有什么价值?我想IConnection curResult=provider.ParseConfiguration(fs)应该是
IConfiguration curResult=provider.ParseConfiguration(fs)
并且应该调用
Connect
方法将
Iconfiguration
转换为
IConnection
。因此,我很难理解这段代码背后的想法。消费者是什么?他们是否将使用
KeyValuePair
集合?他们将如何使用iConfiguration