1

実行時に基本クラスを派生クラスにキャストするにはどうすればよいですか。私がやろうとしていることは次のとおりです。特定のタイプのメッセージにサブスクライバーが割り当てられているサブスクリプションを保持するシステムが必要です。メッセージが受信されると、このシステムに転送されます。このシステムは、この種のメッセージのサブスクライバーが存在するかどうかを調べます。存在する場合、メッセージはサブスクライバーに転送されます。Response という抽象基本クラスがあり、その後に抽象レイヤーが続きます。AFResponse : 応答、そして実際の (具体的な) 実装があります。応答 (抽象) : AFResponse (抽象) : AFIncommingMessage (具体的)。

パケットが受信されると、MessageBuilder に送られ、メッセージ タイプが解決され、次のようにビルドされます。

public static T Build<T>(Packet packet) where T : Response
{
    Type resolvedType;
    if (!dependencyMap.TryGetValue(packet.MessageId, out resolvedType))
    {
        var str = String.Format("Could not resolve message. Message info: CMD0: {0}, CMD1: {1}, MessageID: {2}",
                                packet.Cmd0, packet.Cmd1, packet.MessageId);
        Debug.WriteLine(str);

        throw new ResolutionFailedException(str);
    }

    ConstructorInfo firstConstructor = resolvedType.GetConstructors().First();

    return  (T) firstConstructor.Invoke(new object[] {packet});
}

次に、メッセージが非同期の場合、メッセージ タイプのサブスクリプションを保持する MessageBinder に転送されます。

private void OnAsyncResponseReceived(Response response)
{
    messageBinder.Forward(response);
}

MessageBinder クラスは次のように実装されます。

public class MessageBinder
{
    private class Subscriber<T> : IEquatable<Subscriber<T>> where T : Response
    {
        ...
    }

    private readonly Dictionary<Type, IEnumerable> bindings;

    public MessageBinder()
    {
        this.bindings = new Dictionary<Type, IEnumerable>();
    }

    public void Bind<TResponse>(ushort shortAddress, Action<ZigbeeAsyncResponse<TResponse>> callback)
        where TResponse : Response
    {
        HashSet<Subscriber<TResponse>> subscribers = this.GetSubscribers<TResponse>();
        if (subscribers != null)
        {
            subscribers.Add(new Subscriber<TResponse>(shortAddress, callback));
        }
        else
        {
            var subscriber = new Subscriber<TResponse>(shortAddress, callback);
            this.bindings.Add(typeof(TResponse), new HashSet<Subscriber<TResponse>> { subscriber });
        }
    }

    public void Forward<TResponse>(TResponse response)
        where TResponse : Response
    {
        var subscribers = this.GetSubscribers<TResponse>();
        if (subscribers != null)
        {
            Subscriber<TResponse> subscriber;

            var afResponse = response as AFResponse;
            if (afResponse != null)
            {
                subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
            }
            else
            {
                subscriber = subscribers.FirstOrDefault();
            }

            if (subscriber != null)
            {
                Debug.WriteLine("Forwarding received async response of type " + response.GetType().Name);
                subscriber.Forward(response);
            }
        }
    }

    private HashSet<Subscriber<TResponse>> GetSubscribers<TResponse>() where TResponse : Response
    {
        IEnumerable subscribers;
        this.bindings.TryGetValue(typeof(TResponse), out subscribers);

        return (HashSet<Subscriber<TResponse>>)subscribers;
    }
}

MessageBinder クラスのメソッド GetSubscribers() が呼び出されると、TResponse の型が Response である基本クラスの型であるため、サブスクライバーが見つからないため、問題が発生します。だから私がする必要があると思ったのは、どういうわけか実際のタイプのメッセージを Forward メソッドに渡し、そのタイプが実際のタイプの応答になるようにすることです。

メソッドを次のように変更しました。

private void OnAsyncResponseReceived(Response response)
{
    messageBinder.Forward((dynamic)response);
}

それは機能しますが、どういうわけか、これを行うためのより良い(?)方法があると思います...またはこれが実際の唯一の解決策ですか?全体的なデザインを変更する必要があるかもしれませんが、わかりません...この種の問題に初めて直面しましたが、これが思い付きました。

私は提案、批評家、そしてもちろん、可能な限り最善の解決策を受け入れます:)私の解決策とは異なり、より効率的である場合、これをどのように実装しますか? 助けてくれてありがとう!

4

1 に答える 1

1

メッセージ自体を介してサブスクライバーへの要求をルーティングすることを検討しましたか? 必ずしも直接的である必要はありません。基本クラスでは、ある種のユーティリティ オブジェクトを公開するプロパティをオーバーライドする子クラスが必要になる場合があります。ユーティリティ オブジェクトは、メッセージ タイプに厳密に型指定することができ、サブスクライバーを取得するために厳密に型指定された呼び出しを行うことができます。

作成したメソッドを (サブスクライバーを取得するために) 引き続き使用できます。ユーティリティ クラスには、厳密に型指定された呼び出しを行う機能しかありません。

編集:

これはどう?

private void OnAsyncResponseReceived<T>(T response) where T : Response {
  messageBinder.Forward(response);
}

元のコードの問題は、応答変数が厳密に型指定されていることです。動的な「強く型付けされていない」それへのキャスト。アプリケーション全体で行うようにジェネリック メソッドを使用すると、ジェネリック型が Response から継承しなければならないという制約が追加され、同じ結果が得られますが、動的型付けが組み込まれています。正直なところ、ダイナミックへのキャストはかなり賢いと思います。:)

于 2012-07-06T12:26:54.747 に答える