web-dev-qa-db-fra.com

L'objet doit implémenter IConvertible (InvalidCastException) lors de la conversion en interface

J'essaie de convertir un objet d'un certain type en une interface qu'il implémente à l'aide de Convert.ChangeType(), mais une variable InvalidCastException est émise car l'objet doit implémenter IConvertible .

Les types:

public IDocumentSet : IQueryable {}

public IDocumentSet<TDocument> : IDocumentSet, IQueryable<TDocument> {}

public XmlDocumentSet<TDocument> : IDocumentSet<TDocument> {}

Extrait du code où l'erreur se produit:

private readonly ConcurrentDictionary<Type, IDocumentSet> _openDocumentSets = new ConcurrentDictionary<Type, IDocumentSet>();

public void Commit()
{
    if (_isDisposed)
        throw new ObjectDisposedException(nameof(IDocumentStore));

    if (!_openDocumentSets.Any())
        return;

    foreach (var openDocumentSet in _openDocumentSets)
    {
        var documentType    = openDocumentSet.Key;
        var documentSet     = openDocumentSet.Value;

        var fileName        = GetDocumentSetFileName(documentType);
        var documentSetPath = Path.Combine(FolderPath, fileName);

        using (var stream = new FileStream(documentSetPath, FileMode.Create, FileAccess.Write))
        using (var writer = new StreamWriter(stream))
        {
            var documentSetType     = typeof (IDocumentSet<>).MakeGenericType(documentType);
            var writeMethod         = typeof (FileSystemDocumentStoreBase)
                                        .GetMethod(nameof(WriteDocumentSet), BindingFlags.Instance | BindingFlags.NonPublic)
                                        .MakeGenericMethod(documentSetType);
            var genericDocumentSet  = Convert.ChangeType(documentSet, documentSetType); <-------

            writeMethod.Invoke(this, new[] {writer, genericDocumentSet});
        }
    }
}

Maintenant, je ne comprends pas pourquoi cela se produit exactement (car XmlDocumentSet n'est pas un type de valeur) et XmlDocumentSet<'1> implémente IDocumentSet<'1>. Est-ce que je manque quelque chose? Ou y a-t-il un moyen plus facile de réaliser ce que je fais?

11
artganify

L'interface IConvertible est conçue pour permettre à une classe de se convertir en toute sécurité à un autre type. L'appel Convert.ChangeType utilise cette interface pour convertir en toute sécurité un type à un autre.

Si vous ne connaissez pas les types au moment de la compilation, vous serez forcé d'essayer un transtypage à l'exécution. Ceci est discuté dans une question très similaire ici Convertir une variable en dactylographie uniquement connue au moment de l'exécution? .

2
PhillipH

Implémenter IConvertible est très pénible pour de tels scénarios légitimes et, à mon avis, une perte de temps de développement. Le mieux est d'implémenter une méthode abstraite dans la classe de base, que votre classe dérivée implémentera pour se retourner elle-même. ci-dessous est l'exemple.

//implement this in base class
        protected abstract BaseDocumentTypeMap<ID> ConvertDocType(T doc);

//usage of the abstract code
            BaseDocumentTypeMap<ID> beDocType;

                //loop through all the document types and check if they are enabled
                foreach(T doc in result)
                {
                    beDocType = ConvertDocType(doc);
                    //some action
                }


//implement this in the derived class
        protected override BaseDocumentTypeMap<int> ConvertDocType(DocumentTypeMap doc)
        {
            return doc;
        }

Ce travail parfaitement et sans besoin de IConvertible douloureux. Dans l'exemple ci-dessus, la classe de base implémente une interface avec <ID, T> et la classe dérivée comporte une référence à la classe DocumentTypeMap. La classe DocumentTypeMap implémente l'interface avec <ID>.

0
Kalpesh Popat