Object must implement IConvertible (InvalidCastException) while casting to interface

62,545

Solution 1

The IConvertible interface is designed to allow a class to safely convert itself to another Type. The Convert.ChangeType call uses that interface to safely convert one type to another.

If you do not know the Types at compile time then you will be forced to attempt a runtime cast. This is discussed in a very similar question here Convert variable to type only known at run-time?.

Solution 2

Implementing IConvertible is a lot of pain for such legitimate scenarios, and in my opinion waste of precious development time. Best is to implement an abstract method in the base class, which your derived class will implement to return itself. below is the example.

//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;
        }

This work perfectly and without a need of painful IConvertible. in the above example the base class is implementing an interface with <ID, T> and derived class has a reference to the DocumentTypeMap class, and DocumentTypeMap class is implementing the interface with <ID>

Share:
62,545
artganify
Author by

artganify

Updated on July 17, 2022

Comments

  • artganify
    artganify almost 2 years

    I'm trying to cast an object of a certain type to an interface it implements using Convert.ChangeType(), however an InvalidCastException gets thrown because the object must implement IConvertible.

    The types:

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

    Excerpt from code where the error happens:

    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});
            }
        }
    }
    

    Now, I'm failing to understand why exactly this happens (as XmlDocumentSet is not a value type) and XmlDocumentSet<'1> implements IDocumentSet<'1>. Am I missing something? Or is there an easier way to achieve what I'm doing?

  • Philip
    Philip about 4 years
    Although I didn't try your method, but thank you for warning me about how painful this is