Company logo
  • Jobs
  • Bootcamp
  • About Us
  • For professionals
    • Home
    • Jobs
    • Courses
    • Questions
    • Teachers
    • Bootcamp
  • For business
    • Home
    • Our process
    • Plans
    • Assessments
    • Payroll
    • Blog
    • Sales
    • Calculator

0

186
Views
Convert abstract type to 7 possible other types

I have the following abstract class:

public abstract partial class AsyncResult
{
}

And 7 other classes that inherit from it(here are only 2 for reference):

public partial class AsyncSearchResult : AsyncResult
{
    private SearchResult searchResultField;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Namespace="urn:core_2020_2.platform.webservices.netsuite.com", Order=0)]
    public SearchResult searchResult
    {
        get
        {
            return this.searchResultField;
        }
        set
        {
            this.searchResultField = value;
        }
    }
}

public partial class AsyncGetListResult : AsyncResult
{
    private ReadResponseList readResponseListField;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(Order=0)]
    public ReadResponseList readResponseList
    {
        get
        {
            return this.readResponseListField;
        }
        set
        {
            this.readResponseListField = value;
        }
    }
}

I have a method that returns the abstract type and I need to cast it to one of the specific types in order to access the desired property.

I can't find a way to cast it at compile time without writing 7 ifs for each case and duplicate the code a lot.

Appreciate any kind of help.

8 months ago · Santiago Trujillo
2 answers
Answer question

0

I like using the visitor pattern for this:

public interface IAsyncResultVisitor
{
    void Accept(AsyncSearchResult searchResult);
    void Accept(AsyncGetListResult getListResult);
    // Etc...
}

public abstract partial class AsyncResult
{
    public abstract void Visit(IAsyncResultVisitor visitor);
}

public partial class AsyncSearchResult : AsyncResult
{
    // ...
    public override void Visit(IAsyncResultVisitor visitor) => visitor.Accept(this);
}

Then:

public class AsyncResultProcessor : IAsyncResultVisitor
{
    public void ProcessAsyncResult(AsyncResult asyncResult)
    {
        asyncResult.Visit(this);
    }

    public void Accept(AsyncSearchResult searchResult)
    {
        // Access strongly-typed members of searchResult
    }

    public void Accept(AsyncGetListResult getListResult)
    {
        // Access strongly-typed members of getListResult
    }
}

Note that switch statements and switch expressions also help here, although they don't ensure that you've covered every single case (as the visitor pattern does):

switch (asyncResult)
{
    case AsyncSearchResult searchResult:
        // ...
        break;
}
8 months ago · Santiago Trujillo Report

0

I would've implemented visitor in different way. Problem with @canton7 implementation is that data have to know how people should work with it, but it's just plain data!

First: AsyncResult should have private protected constructor, so no other assemblies could "implement" this abstract class

public abstract class AsyncResult
{
    private protected AsyncResult() {}
}

public sealed class ResultOne : AsyncResult()
{
    public string Data { get; set; }
}
// 6 more to go

Make interface to match all types

public interface IAsyncResultVisitor
{
    void Visit(AsyncResult result);
    void VisitResultOne(ResultOne result);
    void VisitResultTwo(ResultTwo result);
    // 5 more to go
}

And create default visitor that's gonna have properties for each type containing visit strategy

public sealed class LambdaAsyncResultVisitor : IAsyncResultVisitor
{
    public Action<ResultOne> OnVisitOne { get; set; } 
    public void VisitResultOne(ResultOne result)
    {
       if (OnVisitOne is null)
           throw new InvalidOperationException("...");
       OnVisitOne.Invoke(result);
    }
    // 6 more to go
}

This would allow us to use it this way

AsyncResult result = ...;

string someString = "";
new LambdaAsyncResultVisitor
{
   OnVisitOne = r => someString = r.Data,
   OnVisitTwo = r => someString = "two",
   OnVisitThree => _ =>  throw new Exception("I know that is is impossible"),
}.Visit(result);

HandleSomeString(someString);

This gives much more flexibility than default visitor pattern

8 months ago · Santiago Trujillo Report
Answer question
Find remote jobs