43

インターフェイスを実装するオブジェクトのリストと、そのインターフェイスのリストがあります。

public interface IAM
{
    int ID { get; set; }
    void Save();
}

public class concreteIAM : IAM
{
     public int ID { get; set; }
     internal void Save(){
     //save the object
     }

    //other staff for this particular class
}

public class MyList : List<IAM>
{
    public void Save()
    {
        foreach (IAM iam in this)
        {
            iam.Save();
        }
    }

    //other staff for this particular class
}

コンパイラはすべてのインターフェイスメンバーをパブリックにする必要があるため、前のコードはコンパイルされません。

internal void Save(){

しかし、DLLの外部からのを許可したくないので、をConcreteIAM介してのみ保存する必要がありMyListます。

これを行う方法はありますか?

Update#1:こんにちは、これまでの回答に感謝しますが、どれも私が必要としているものではありません。

インターフェイスはパブリックである必要があります。これは、dllの外部からのクライアントが使用する署名でありID、他のプロパティとともに、例を単純にするためにわざわざ記述しなかったためです。

IAMAndrew、解決策は、メンバー+保存を含む別のオブジェクトを作成するためのファクトリを作成することではないと思います。私はまだ考えています...他のアイデアはありますか?

4

13 に答える 13

50

インターフェイスの目的を理解していないと思います。インターフェイスはコントラクトです。オブジェクトが特定の方法で動作することを指定します。オブジェクトがインターフェイスを実装している場合、それはインターフェイスのすべてのメソッドが実装されていることを信頼できることを意味します。

ここで、あなたが求めているようなインターフェイスがあった場合に何が起こるかを考えてみましょう - public ですが、内部メンバーが 1 つあります。それはどういう意味ですか?外部オブジェクトはパブリック メソッドのみを実装できますが、内部メソッドは実装できません。このような外部オブジェクトを取得すると、パブリック メソッドのみを呼び出すことができますが、内部メソッドは呼び出すことができません。これは、オブジェクトがそれを実装できないためです。つまり、契約は履行されません。すべてのメソッドが実装されるわけではありません。

この場合の解決策は、インターフェイスを 2 つに分割することだと思います。1 つのインターフェイスは public になり、それが外部オブジェクトが実装するものになります。もう 1 つのインターフェイスは内部的なもので、Save()独自のメソッドやその他の内部メソッドが含まれます。おそらく、この 2 番目のインターフェイスは最初のインターフェイスから継承することさえできます。独自の内部オブジェクトは、両方のインターフェースを実装します。このようにして、外部オブジェクト (内部インターフェースを持たないオブジェクト) と内部オブジェクトを区別することさえできます。

于 2008-12-15T09:55:49.673 に答える
36

内部的な別のインターフェイスを作成し、メソッドの明示的な実装を使用します。

internal interface InternalIAM
{
    void Save();
}

public class concreteIAM : InternalIAM
{
    void InternalIAM.Save()
    {
    }
}
于 2008-12-15T10:23:37.933 に答える
9

内部メンバーとパブリック メンバーを 2 つの別個のインターフェイスに分割するのが最善の方法だと思います。インターフェイスを継承する場合でも、メンバーをパブリックに宣言できますが、内部メンバーの可視性はインターフェイス自体の可視性によって決まります。

using System;

public interface IPublic
{
    void Public();
}

internal interface IInternal : IPublic
{
    void Internal();
}

public class Concrete : IInternal
{
    public void Internal() { }

    public void Public() { }
}
于 2008-12-15T14:15:10.883 に答える
8

ここでインターフェースを使うべきではないと思います。たぶん、次のような抽象的なベースを使用する必要があります。

public abstract class AM
{
    public int ID { get; set; }
    internal abstract void Save();
}

public class concreteIAM : AM
{
    internal override void Save()
    {
        //Do some save stuff
    }
}

それでもこれを行うことができます:

public class AMList : List<AM>
{
    public void SaveItems()
    {
        foreach (var item in this)
        {
            item.Save();
        }
    }
}
于 2008-12-15T04:18:05.860 に答える
4

これはまさに、私の記事Friends and internal interface members at no cost withcoding to interfaces で話していることです。

記事のエッセンス

public class Internal
{
    internal Internal() { }
}

public interface IAM
{
    int ID { get; set; }
    void Save(Internal access);
}

Save()これ以降、クラスのインスタンスはInternalアセンブリによってのみ作成できるため、アセンブリのみがメソッドを呼び出すことができます。

于 2012-06-21T19:35:21.627 に答える
1

外部の呼び出し元が Save() メソッドを呼び出せないようにする場合は、concreteIAM クラス全体を内部にしないでください。

または、インターフェイスではなくクラスをパブリックにしたい場合は、インターフェイス全体を内部にします。パブリッククラスに内部インターフェースを追加できると思います(ただし、試したことはありません...)

于 2008-12-15T05:01:57.800 に答える
1

アイテムの保存を、アセンブリの内部にある別のクラスのセットに分けたいと思うかもしれません:

internal interface IAMSaver { void Save(IAM item); }

internal class AMSaverFactory {
  IAMSaver GetSaver(Type itemType) { ... }
}

public class MyList : List<IAM>
{
  public void Save()
  {
    foreach (IAM itemin this)
    {
      IAMSaver saver = SaverFactory.GetSaver(item.GetType());
      saver.Save(item)
    }
  }
}
于 2008-12-15T05:16:31.470 に答える
1

インターフェイス メンバーは public.. である必要があり、何か他のものが必要かどうかを検討する必要があります。この場合、あなたは

  • Save をインターフェースメンバーにしたい
  • 同じアセンブリにある MyList という別のクラスを介してのみ保存を許可したい
  • Save が外部から (親アセンブリの外部の他のクラスから) 呼び出されることを許可しない

設計上の考えはさておき、明示的なインターフェイスの実装を介してこれを行うことができます。

    internal interface IPersist
    {
        void Save();
    }
    public class Concrete : IPersist
    {
        void IPersist.Save()
        {
            Console.WriteLine("Yeah!");
        }
    }

// Mylist.cs in the same assembly can still call save like
public void SaveItems()
    {
        foreach (IPersist item in this)
        {
            item.Save();
        }
    }

IPersist は内部的なものであり、親アセンブリの外部では使用できません。また、明示的に実装されているため、IPersist 参照なしで呼び出すことはできません。

new Concrete().Save();     // doesn't compile. 'Concrete' does not contain a definition for 'Save'

更新あなたの最新の回答から、より多くの制約があります。インターフェイスはパブリックである必要があり、一般に公開されてはならない Save メソッドが含まれている必要があります。設計図に戻るといいのですが...何かがおかしいようです。私見単一のインターフェースでこれを行うことはできません。public と internal の 2 つのインターフェイスに分割するのはどうでしょうか。

于 2008-12-15T05:31:25.900 に答える
-2

ここで同じ問題について疑問に思っていて、この質問に出くわしました...

これについて考えてみると、そもそもインターフェイスに内部メソッドは必要ないことがわかりました。

Concrete Class を介してアクセスし、Contract を Out-Side コードに残すことができます。

あなたの例では:

    public interface IAM
    {
            int ID { get; set; }
    }

    public class concreteIAM : IAM
    {
             public int ID{get;set;}
             internal void Save(){
             //save the object
             }

            //other staff for this particular class
    }

    public class MyList : List<IAM>
    {
            public void Save()
            {
                    foreach (concreteIAM iam in this)
                    {
                            iam.Save();
                    }
            }
            //other staff for this particular class
    }
于 2012-01-11T17:12:44.730 に答える