Je cherche un autre moyen de résoudre ce problème sur lequel je suis tombé.

L'erreur CS0738 «MyImplementationClass» n'implémente pas le membre d'interface «IMyAbstractClass .MyEvent». 'MyAbstractClass .MyEvent' ne peut pas implémenter 'IMyAbstractClass .MyEvent' car il n'a pas le type de retour correspondant de 'MyDelegate '

Je travaille sur un projet qui m'oblige à créer un assemblage d'interfaces pour permettre à une autre équipe de faire son travail sans connaître l'implémentation desdites interfaces.

J'ai donc créé un assembly "Project.Interfaces" où j'ai déclaré toutes mes interfaces publiques. Et un assembly "Project.Implementation" où j'ai déclaré toutes mes classes internes. Chaque classe / structure implémentant sa propre interface, qui hérite d'une interface de base partout où la classe hérite également d'une classe de base.

J'avais besoin de déclarer un événement dans l'une des classes, en utilisant un paramètre de type contraint. Cette classe est abstraite et une classe d'implémentation spécialisée existe.

Voici un exemple de code qui permet de reproduire le problème. J'utilise .NET Framework 4.5.2:

public interface IMyDataType
{ 
}

internal class MyDataTypeClass : IMyDataType
{
}

public delegate void MyDelegate<MyDataType>(MyDataType argument)
    where MyDataType : IMyDataType;

public interface IMyAbstractClass<MyDataType>
    where MyDataType : IMyDataType
{
    event MyDelegate<MyDataType> MyEvent;
}

internal abstract class MyAbstractClass<MyDataType> : IMyAbstractClass<MyDataType>
    where MyDataType : IMyDataType
{
    public event MyDelegate<MyDataType> MyEvent;
}

public interface IMyImplementationClass : IMyAbstractClass<IMyDataType>
{
}

internal class MyImplementationClass : MyAbstractClass<MyDataTypeClass>, IMyImplementationClass
{
}

Cela se produit également si le membre est une propriété de type MyDataType, au lieu d'un délégué.

Je comprends la cause de l'erreur du compilateur, MyDelegate ne peut pas être converti en MyDelegate .

Mais MyDataTypeClass EST une implémentation de IMyDataType. Donc, appeler une méthode qui reçoit un paramètre IMyDataType, passer un objet / struct typé MyDataTypeClass est possible.

Pour contourner ce problème, j'ai dû réimplémenter le membre "MyEvent" dans la classe d'implémentation, en tant qu'implémentation explicite du membre des interfaces, et chaîner l'événement d'origine, ce qui nécessite le nettoyage de l'événement de chaînage.

internal class MyImplementationClass : MyAbstractClass<MyDataTypeClass>, IMyImplementationClass
{
    event MyDelegate<IMyDataType> IMyAbstractClass<IMyDataType>.MyEvent
    {
        add
        {
            var chainedEvent = new MyDelegate<MyDataTypeClass>(value);
            chainedEvents.Add(value, chainedEvent);
            base.MyEvent += chainedEvent;
        }

        remove
        {
            if (chainedEvents.ContainsKey(value))
            {
                var chainedEvent = chainedEvents[value];
                chainedEvents.Remove(value);
                base.MyEvent -= chainedEvent;
            }
        }
    }

    private IDictionary<MyDelegate<IMyDataType>, MyDelegate<MyDataTypeClass>> chainedEvents =
                new Dictionary<MyDelegate<IMyDataType>, MyDelegate<MyDataTypeClass>>();
}

Pour les propriétés, une réimplémentation similaire chaînant la propriété d'origine est requise. Un simple cast implicite suffit et aucun nettoyage n'est nécessaire.

J'ai deux problèmes avec cette solution:

  1. Je ne sais pas pourquoi, mais cela ne fonctionne pas lorsque vous utilisez des types référencés par valeur pour le & lt; MyDataType & gt; paramètre. Par exemple: structure interne MyDataTypeStruct: IMyDataType

    Erreur CS0123 Aucune surcharge pour 'MyDelegate & lt; IMyDataType & gt; .Invoke (IMyDataType) 'correspond au délégué' MyDelegate & lt; MyDataTypeStruct & gt; '

  2. Ma classe d'implémentation du monde réel hérite deux fois de la classe abstraite originale (abstract - & gt; abstract légèrement plus spécialisé - & gt; classe spécialisée). J'ai donc dû répéter exactement le même enchaînement de l'événement en classe moyenne et en classe finale.

Des solutions déjà essayées et non acceptées:

  1. Déplacement de l'événement vers l'interface des classes d'implémentation finale, car l'autre équipe a besoin de connaître l'événement à partir du résumé le plus bas l'interface des classes.

  2. Changement de l'interface des classes finales en IMyAbstractClass & lt; MyDataTypeClass / MyDataTypeStruct & gt; au lieu de IMyAbstractClass & lt; IMyDataType & gt ;, car cela provoque une "accessibilité incohérente", me forçant à rendre la classe / structure publique et à fournir l'assembly "Project.Implementation" à l'autre équipe, ce qui n'est pas le but prévu. Cela me forcerait également à implémenter l'événement (bien que l'auto-implémentation soit possible) dans chaque classe sœur.

Alors, est-ce que quelqu'un connaît un moyen plus convivial pour résoudre ce problème?

1
podostro 31 déc. 2015 à 22:43

2 réponses

Meilleure réponse

L'approche consistant à publier le IMyImplementationClass en tant que générique était également interdite. Cela m'a obligé à rendre le type de données public.

Comme l'a dit StriplingWarrior, il n'y avait aucun besoin réel pour le délégué d'avoir un paramètre typé. Donc je viens de le déclarer comme public delegate void MyDelegate(IMyDataType argument) et cela évite d'avoir à enchaîner l'événement dans chaque classe héritière.

Merci pour l'aide!

0
podostro 4 janv. 2016 à 02:11

Que diriez-vous de rendre IMyImplementationClass générique?

public interface IMyImplementationClass<MyDataType> : IMyAbstractClass<MyDataType>
    where MyDataType : IMyDataType
{
}

internal class MyImplementationClass : MyAbstractClass<MyDataTypeClass>, IMyImplementationClass<MyDataTypeClass>
{
}
1
StriplingWarrior 31 déc. 2015 à 23:31