30

MembershipUser から継承するカスタム CustomMembershipUser があります。

public class ConfigMembershipUser : MembershipUser
{
    // custom stuff
}

Linq-to-SQL を使用してデータベースから読み取り、User エンティティを取得しています。この関数を MembershipUser として作成するために、明示的な変換を定義しました。

public static explicit operator MembershipUser(User user)
{
    DateTime now = DateTime.Now;

    if (user == null) return null;

    return new MembershipUser("MagicMembershipProvider", 
                              user.DisplayName, user.Id, 
                              user.Email, "", "", true, false, 
                              now, now, now, now, now);
}

このキャストはうまくいきます:

MembershipUser memUser = (MembershipUser) entityUser;

ただし、CustomMembershipUser への 2 番目のキャストは失敗します。

MembershipUser memUser = (MembershipUser) entityUser;
CustomMembershipUser custUser = (CustomMembershipUser) memUser;

キャストを変更すると

CustomMembershipUser custUser = memUser;

暗黙のキャストが機能しないが、明示的なキャストが存在するというインテリセンス エラーが表示されます。

... それに加えて、基本クラスからサブクラスへのキャストを定義できないようです。私はそれを試しましたが、失敗しました。私が最も理解していないのは、なぜ基本クラスからサブクラスへのキャストが失敗するのかということです。定義上、サブクラスには基本クラスのすべてのプロパティがあるため、何が問題なのですか。

編集

MembershipUser から CustomMembershipUser への明示的なキャストを定義しようとしました (最初にキャスト用のプライベート コンストラクターを定義しました)。

private ConfigMembershipUser(MembershipUser user)
    : base(user.ProviderName, user.UserName, user.ProviderUserKey, user.Email,
           user.PasswordQuestion, user.Comment, user.IsApproved, user.IsLockedOut,
           user.CreationDate, user.LastLoginDate, user.LastActivityDate, 
           user.LastPasswordChangedDate, user.LastLockoutDate)
    {
        // initialize extended CustomMembershipUser stuff here
    }

次に、カスタム キャストを定義しました。

public static explicit operator CustomMembershipUser(MembershipUser user)
{
     return new CustomMembershipUser(user);
}

次のエラーが発生しました。

'CustomMembershipUser.explicit operator CustomMembershipUser (System.Web.Security.MembershipUser)':基本クラスとの間のユーザー定義の変換は許可されていません

それで...基本クラスからサブクラスにキャストできませんか?

4

3 に答える 3

39

逆に言えば、基本クラスのオブジェクトからサブクラスへのキャストは常に失敗します。これは、基本クラスには基本クラスのプロパティのみが含まれているためです(サブクラスは含まれていません)。

あなたが言うように、サブクラスは基本クラスのすべてのプロパティ(「is-a」基本クラスオブジェクト)を持っているので、サブクラスから基本クラスへのキャストは常に成功しますが、その逆はありません。

言い換えれば、すべてのヒョウを猫と考えることはできますが、任意の猫を連れてヒョウのように扱うことはできません(そもそもヒョウでない限り)。

CustomMembershipUserオブジェクトの代わりにオブジェクトを返すか、新しいオブジェクトを作成してMembershipUsersをCustomMembershipUsersに変換する別の明示的なキャストMembershipUser別関数を定義する必要があります。CustomMembershipUserオブジェクトをどこからともなく取得することはできません。最初に、直接または(基本クラスではなく)のサブクラスをインスタンス化することによって作成されています。CustomMembershipUserCustomMembershipUser

編集:

サブクラスへの明示的なキャストを定義することについて間違っていました。これは不可能です(表示されるエラーが示すように)。あなたは今、この質問の質問者とまったく同じ状況にあるようです。キャストは実際にはここに行く方法ではありません-CustomMembershipUser最初にオブジェクトを作成するか(オブジェクトとして直接使用可能)、またはを受け入れてを作成するMembershipUser変換メソッドを記述します。MembershipUserCustomMembershipUser

基本オブジェクトからサブクラスオブジェクトにキャストすることが理にかなっているのは、それがすでにサブクラスオブジェクトである場合だけです(ただし、それを保持する変数は基本クラスタイプです)。

于 2011-03-09T00:50:54.580 に答える
14

サブタイプはスーパータイプのインスタンスであるため、MembershipUserタイプの変数は、CustomMembershipUserタイプのオブジェクトを保持できます。しかし、その逆は真実ではありません。

CustomMembershipUserには、MembershipUserにないメンバーが含まれている可能性があります。したがって、CustomMembershipUser型の変数は、MembershipUser型のオブジェクトを保持できません。そうしないと、コードが含まれていないメンバーの1つにアクセスしようとする可能性があります。

これは失敗します:

CustomMembershipUser custUser = memUser; 

あなたはこれでそれをフォローアップするかもしれないので:

custUser.CustomStuff();   // Oops! Can't call CustomStuff() on a MembershipUser object!

「明示的なキャストが存在します」メッセージ

「明示的なキャストが存在します」というメッセージが表示されるのは、UserからMembershipUserへのキャストを作成したためではありません。(ここでは、ユーザータイプはまったく関係ありません。)これは、スーパータイプからサブタイプへの明示的なキャストが常に存在するためです。それは言語設計の一部です。これは、オブジェクトがサブタイプであり、一致する変数を使用することがわかっているシーンリオをサポートするためです。ただし、ターゲットタイプではないオブジェクトでその明示的なキャストを使用すると、(経験したように)ランタイムエラーが発生します。

キャストが失敗する理由についての詳細な説明

C#では、すべてのオブジェクトにタイプがあります。そのタイプは、オブジェクトの存続期間中は変更できません。たとえば、従業員を作成すると、それは常に永遠に、またはガベージコレクションまでは常に従業員になります。

public class Person
{
    public string Name {get; private set;}
    public Person(string name)
    {  Name = name; }
}
public class Employee : Person
{
    public DateTime HireDate {get; private set;}
    public Employee(string name, DateTime hireDate)
        : base (name)
    {    HireDate = hireDate;  }
}

Person型の変数がある場合、EmployeeはPersonであるため、その変数はEmployeeオブジェクトを保持できます。

Employee mike = new Employee("Michael", DateTime.Now);
Person myBestBud = mike;

これは常に機能するため、暗黙のキャストです。Person変数は、常にEmployeeオブジェクトを保持できます。これは、継承により、使用しようとするPersonのすべてのメンバーが使用可能になることをシステムが認識しているためです。

Console.WriteLine("Dude's name: " + myBestBud.Name);

それでは、別の方法で試してみましょう。

Person johnny = new Person("Johnny Johnson");
Employee newHire = johnny;  // ERROR - Attempt to assign...etc.  An explicit cast is available...

これによりエラーが発生します。コンパイラーはPerson変数にEmployeeオブジェクトが含まれていることを保証できないため、PersonからEmployeeへの暗黙のキャストはありません。そのため、コンパイル時エラーが発生します。それでは、明示的なキャストを試してみましょう。

Employee newHire = (Employee)johnny;

これは問題なくコンパイルされます。Person変数がEmployeeオブジェクトを保持することがあるため、これはコンパイラーによって許可されます。ただし、これは実行時に失敗します。これが失敗する理由は、変数johnnyに従業員がいないため、1人のように扱うことができないためです。したがって、無効なキャスト例外がスローされます。

無効なキャスト例外がスローされなかった場合は、次のようなことを試みることができます。

Console.WriteLine("Hired on: " + newHire.HireDate);

ただし、オブジェクトは実際には従業員ではなく個人であるため、プロパティは存在しません。

したがって、サブタイプからスーパータイプへの暗黙のキャストがあることがわかります。これは、常に成功し、問題が発生しないためです。スーパータイプからサブタイプへの明示的なキャストがあります。これは、オブジェクトのランタイムタイプが変数と割り当て互換である場合にのみ機能するためです。プログラマーは、それがいつ機能するか、いつ機能しないかを知っており、機能するときにのみキャストを行うことが期待されています。それ以外の場合、ランタイムは無効なキャストを検出し、例外をスローします。

これで、ユーザーは、あるタイプから別のタイプにキャストするために使用できるカスタム変換演算子を作成できる場合があります。その場合、ターゲットタイプのまったく新しいオブジェクトが作成されます。ただし、継承階層のキャストはすでにC#コンパイラによって提供されているため、継承階層の上下でこれを行うことはできません。カスタム変換演算子を実行するには、ソースまたはターゲットのタイプが他のタイプの祖先または子孫であってはなりません。

于 2011-03-09T00:54:35.117 に答える
0

キャストを行うことは可能ですが、オブジェクトのプロキシを解除する必要があります。これを行う最も簡単な方法は、同じオブジェクトを返す自分自身への呼び出しを作成することです。継承戦略

于 2012-05-23T16:29:38.047 に答える