web-dev-qa-db-ja.com

インターフェースの内部メンバー

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

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を介してのみ保存する必要があります。

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

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

インターフェースは、dllの外部からのクライアントが使用するシグネチャであり、IDとその他のプロパティを単純にするためにサンプルで記述しなかったため、パブリックにする必要があります。

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

35
fer

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

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

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

38
Vilx-

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

internal interface InternalIAM
{
    void Save();
}

public class concreteIAM : InternalIAM
{
    void InternalIAM.Save()
    {
    }
}
26

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

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();
        }
    }
}
9
Nathan W

私は、内部メンバーとパブリックメンバーを2つの別々のインターフェイスに分割することをお勧めします。インターフェイスを継承する場合でも、メンバーをパブリックに宣言できますが、内部メンバーの可視性は、インターフェイス自体の可視性によって決まります。

using System;

public interface IPublic
{
    void Public();
}

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

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

    public void Public() { }
}
7
Andrew Hare

メソッドのアクセシビリティを制御するために内部クラスを使用しないのはなぜですか?

例:

プライマリアセンブリ

_public abstract class Item
{
    public int ID { get; set; }
    protected abstract void Save();

    public class ItemCollection : List<Item>
    {
        public void Save()
        {
            foreach (Item item in this) item.Save();
        }
    }
}
_

あなたの二次アセンブリ

_public sealed class NiceItem : Item
{
    protected override void Save()
    {
        // do something
    }
}
_

このパターンでは、他のアセンブリにSave()メソッドを実装できますが、ItemCollectionの内部クラスであるItemのみがそれを呼び出すことができます。素晴らしいですよね?

1
lubos hasko

外部の呼び出し元がSave()メソッドを呼び出せるようにしたくない場合は、具象IAMクラス全体を内部にしないのはなぜですか?

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

1
Brannon

たぶん、あなたはあなたのアイテムの保存をあなたのアセンブリの内部にあるクラスの異なるセットに分けたいでしょう:

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)
    }
  }
}
1
Andrew Kennan

インターフェースメンバーはパブリックである必要があります。その他は何でも必要です。この場合、あなた

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

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

    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メソッドを含んでいる必要があり、パブリックに利用可能であってはなりません。設計図に戻ると思います...何かが正しくないようです。 IMHO単一のインターフェースでこれを行うことはできません。パブリックインターフェイスと内部インターフェイスの2つのインターフェイスに分割するのはどうですか。

1
Gishu

2つのインターフェースを使用します。

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


internal interface IAMSavable
{
        void Save();
}

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

        //other staff for this particular class
}

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

        //other staff for this particular class
}

Save()の実装は、クライアントがそれを呼び出さないように明示的である必要があります。

0
MZywitza

私も同じような状況で、これが役立つと思いました。 (これが現時点で必要でないかどうかはわかりません)

これは私にとっては正当なケースです。DLLの内部にあるAPIがいくつかあり、一部のAPIはDLLの外部からアクセスできる場合があります。インターフェイスが抽象であるため、クラス、インターフェイスとして定義する代わりに、抽象クラスとして定義できます。

using System.Collections.Generic;

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

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

    //other staff for this particular class
}

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

    //other staff for this particular class
}
0
Rishi Prasad