Click here to Skip to main content
15,881,413 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Idea
The following code uses an overloaded method which expects the base class or the concrete derrived class as parameter.

Problem
The problem is that
C#
FieldTypeBase field = new TextFieldType();
is passed as type FieldTypeBase and calls DoWork(FieldTypes.FieldTypeBase ...)
while
C#
var field = new TextFieldType();
is passed as TextFieldType and calls DoWork(FieldTypes.TextFieldType...).

Question:
Any idea to ensure that the "correct" method is called?

Remark:
I know, this code smells a little bit, and will probably do a refactoring to implement the Worker directly to the Fields. But actually I'm trying to solve exact this example.

What I have tried:

C#
namespace InternalFieldTest
{
    using System.Collections.Generic;
    using FieldTypes;

    public static class Tests
    {
        public static IEnumerable<string> TestWorker()
        {
            FieldTypeBase field1 = new TextFieldType();
            TextFieldType field2 = new TextFieldType();
            
            // failed: DoWork[error:NoWorkerDefined]
            yield return FieldProcessors.DoWork(field1);

            //ok: DoWork[TextFieldType]
            yield return FieldProcessors.DoWork(field2); 

            // ok: DoWork[error:NoWorkerDefined]
            yield return FieldProcessors.DoWork(new UnknownFieldType());
       }
    }
    public static class FieldProcessors
    {
        public static string DoWork(TextFieldType fieldType) => "DoWork[TextFieldType]"; 
        public static string DoWork(FieldTypeBase fieldType) => "DoWork[error:NoWorkerDefined]";
    }

    namespace FieldTypes
    {
        public abstract class FieldTypeBase { }
        public class TextFieldType : FieldTypeBase { }
        public class UnknownFieldType : FieldTypeBase { }
    }

}
Posted
Updated 19-Feb-20 0:58am
v2

I'm offering a more object-oriented solution to your problem. Read about double-dispatch design pattern. In your case this goes like this:

1) I'd remove the static methods. You could adapt this to use the static processors but this is how I would do it:
C#
public interface IFieldProcessors
{
    string DoWork(TextFieldType fieldType); 
    string DoWork(FieldTypeBase fieldType);
}

public class FieldProcessors : IFieldProcessors
{
    public string DoWork(TextFieldType fieldType) => "DoWork[TextFieldType]"; 
    public string DoWork(FieldTypeBase fieldType) => "DoWork[error:NoWorkerDefined]";
}


2) Add a Dispatch method to your field types. Note that this is where the magic happens as for each field type the compiler knows exactly what processor method to call because it knows the type of this. For unknown field it defaults to the base class as there is no more specific overload.
C#
public abstract class FieldTypeBase 
{ 
	public abstract string Dispatch(IFieldProcessors processors);
}

public class TextFieldType : FieldTypeBase
{
	public override string Dispatch(IFieldProcessors processors)
	{
		return processors.DoWork(this);
	}
}

public class UnknownFieldType : FieldTypeBase
{
	public override string Dispatch(IFieldProcessors processors)
	{
		return processors.DoWork(this);
	}
}


3) Test that the correct method is called:
C#
var field1 = new TextFieldType();
var field2 = new UnknownFieldType();	
var processors = new FieldProcessors();
Console.WriteLine(field1.Dispatch(processors));
Console.WriteLine(field2.Dispatch(processors));

The output is like this:
DoWork[TextFieldType]
DoWork[error:NoWorkerDefined]
 
Share this answer
 
v2
Comments
Maciej Los 20-Feb-20 6:57am    
5ed!
Look at your code:
C#
public static string DoWork(FieldTypes.FieldTypeBase fieldType) => "DoWork[error:NoWorkerDefined]";
What your interpret as an error is just the string that you actually return in case the DoWork(FieldTypes.FieldTypeBase fieldType) method is called. Maybe try
C#
public static string DoWork(FieldTypes.FieldTypeBase fieldType) => "DoWork[FieldTypeBase]";

As a side note, do not use var when a call could be ambiguous, use proper strongly-typed declaration instead.
 
Share this answer
 
The correct method is being called.
C#
FieldTypeBase field = new TextFieldType();

Unless you upcast the base class variable when you call the method, the system has to assume that field can contain any derived class, and will always call teh "safe" version - the base class version.

Instead of doing it that way, implement DoWork as a method in the base class (and make the base class abstract) and allow the derived class to implement their own versions. The system will then call the appropriate version for the type of instance that field contains.
 
Share this answer
 
Thanks for your comments.
I have already done it the right way and moved the function to the base class.
The code above is just a test snippet from am bigger system.

I was just wondering why the system does always use the base class version.

From my view the type is clearly defined by
FieldTypeBase field1 = new TextFieldType();

It says field1 can be everythings derrived from FieldTypeBase and is here initialized by the concrete TextFieldType. In reflection it's Type: TextFieldType, Base: FieldTypeBase.

So I would have expected the compiler preferes the assigned type not the declaration type.
 
Share this answer
 
Comments
Richard Deeming 18-Feb-20 14:54pm    
No, the compiler always works from the declared type of the variable. If you declare a variable as the base type, then that's the type the compiler will use.

Imagine a slightly more complicated code:
FieldTypeBase field1 = new TextFieldType();
// Snip 100 lines...
Foo(ref field1);
// Snip 100 lines...
FieldProcessors.DoWork(field1);

In order to know the run-time type of the value in field1, the compiler would have to evaluate every possible run-time path through the code between the assignment and the method call. That's just not feasible.

And if you restricted this "magic" overload resolution to the next line, then you run into a very confusing situation:
FieldTypeBase field1 = new TextFieldType();
FieldProcessors.DoWork(field1); // "DoWork[TextFieldType]"
FieldProcessors.DoWork(field1); // "DoWork[error:NoWorkerDefined]", because it's not the next line

If you want to use TextFieldType in overload resolution, then you either need to declare the variable as a TextFieldType, or use var and let the compiler infer the type from the allocation.

It sounds like you're actually looking for Double dispatch[^], which is often considered to be a "code-smell". You may be able to achieve that using dynamic, or the visitor pattern.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900