4

誰かが私が直面している変換を同封のコードで手伝ってくれますか...私は問題を抱えているコードの行にコメントしました。これはこれを達成するための正しい方法です...私がやろうとしているのは、指定されたタイプの応答を提供されたコールバックに転送することです。

編集1

ResponseとAFResponseは抽象クラスであり、次のようになります。> Response->AFResponse->AFレイヤーメッセージの具体的な実装

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

    private readonly Dictionary<Type, List<Subscriber<Response>>> bindings;

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

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

            // ERROR: cannot convert from 'List<Subscriber<TResponse>>' to 'List<Subscriber<Response>>' ... tried LINQ Cast operator - does not work either
            this.bindings.Add(typeof(TResponse), new List<Subscriber<TResponse>> { subscriber });
        }
    }

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

            if (responseType.IsSubclassOf(typeof (AFResponse)))
            {
                // ERROR: Cannot convert type 'TResponse' to 'AFResponse' ... tried cast to object first, works, but is this the right way?
                var afResponse = (AFResponse)response;
                subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
            }
            else
            {
                subscriber = subscribers.First();
            }

            if (subscriber != null)
            {
                subscriber.Forward(response);
            }
        }
    }

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

        // ERROR: How can I cast List<Subscriber<Response>> to List<Subscriber<TResponse>>?
        return subscribers;
    }
}

助けてくれてありがとう:)

編集2

@Bojanの回答に従ってコードを変更しましたが、機能しています。しかし、私は興味があります。なぜディクショナリはすべての応答メッセージの基本クラスを保持できないのですか?それを達成する方法さえありますか、それとも私は壁に頭を押し込もうとしただけですか?

編集3

今、私は別の問題に直面しています...メッセージが到着すると、それはバイト配列で構成され、メッセージファクトリに送られ、メッセージファクトリが解決してビルドします。

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 MessageNotFoundException(str);
    }

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

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

次に、OnAsyncResponseReceived(Response response)イベントハンドラーが呼び出され、メッセージがあれば、このメッセージのサブスクライバーにメッセージが転送されます。問題は、(応答のサブレイヤーはAFResponse、SystemResponseなどです...)ResponseのサブクラスであるSystemResponseのサブクラスであるSystemResetResponseをサブスクライブする場合、Response(base)タイプallからその応答をキャストする必要があることです。 MessageBinderのフォワーダーがこのメッセージタイプのサブスクライバーを見つけて転送するための具体的なタイプであるSystemResetResponseへの道。

多くのタイプがあり、手でキャストするのはやり過ぎでしょう...これを回避する方法、またはこのタイプのシステムを設計するためのより良い方法はありますか?

編集4

私はこのようなコードを変更しました...これはこれを行うための正しい方法ですか、他のより良い方法がありますか、そして全体として、私は問題を正しい方法で解決しようとしていますか、それともこれを処理するためのより良い方法がありますか?

private void OnAsyncResponseReceived(Response response)
{
    dynamic resolvedResponse = Convert.ChangeType(response, response.GetType());
    messageBinder.Forward(resolvedResponse);
}
4

3 に答える 3

2

List<Subscriber<Response>>問題の一部は、各エントリが無関係なタイプ()であることを期待しながら、辞書を汎用サブスクライバー()を持つものとして宣言しようとしていることですList<Subscriber<TResponse>>。解決策は、実際のタイプを背後に隠すobjectか、次のようにすることIListです。

private readonly Dictionary<Type, object> bindings;

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

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

メソッドは少し簡単な方法でサブクラスをチェックできますが、キャストは次のことを行う必要がありForwardます。AFResponseobject

public void Forward<TResponse>(TResponse response)
    where TResponse : Response
{
    var subscribers = 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.First();
        }

        if (subscriber != null)
        {
            subscriber.Forward(response);
        }
    }
}

アップデート

「すべてのメッセージの基本クラス」を保持するように辞書を宣言していません。Responseはすべての応答の基本クラスであり、Subscriber<Response>無関係なSubscriber<T>タイプですが。これは、とが。から継承している場合でも、List<int>とがどのようにList<string>関連していないかにいくらか類似しています。stringintobject

探しているものは共分散と呼ばれ、一部のインターフェースでのみサポートされます。

たとえば、次の場合:

interface ISubscriber<out T> where T:Response
{}

class Subscriber<T> : ISubscriber<T> where T:Response
{}

あなたができること:

ISubscriber<Response> s = new Subscriber<AFResponse>();

ただし、それでも無関係なタイプになりますList<ISubscriber<Response>>List<ISubscriber<AFResponse>>

于 2012-07-05T07:40:13.607 に答える
2

subscribersリスト内の各アイテムを にキャストする必要がありますSubscriber<TResponse>。このようなものは常に私のために働きます -

private List<T> get<T>()
    {
        List<IEntity> list = new List<IEntity>();
        List<T> genericList = new List<T>();
        foreach (var item in list)
        {
            genericList.Add((T)item);
        }
        return genericList;
    }

それが役に立てば幸い!!

于 2012-07-05T03:04:19.940 に答える
2

すでに提供されている回答に加えて、次のコード行...

    Type responseType = typeof (TResponse);
    if (responseType.IsSubclassOf(typeof (AFResponse)))

次のいずれかに置き換えることができます。

    if (response is AFResponse)

または(より良い)

    AFResponse af = response as AFResponse;
    if (af != null)

EDIT TResponseがジェネリックであることを事前に認識していなかったため、コードを更新しました。IsSubclassOf が一致するタイプの場合に false を返すことも知っていますか?

于 2012-07-05T03:11:34.560 に答える