user1713059
user1713059

Reputation: 1507

Factory of classes implementing interfaces with generic types in C#?

I need to create a general way of handling files. Depending on what needs to be done with the file, specific classes should be used. I use ASP and C#. File is uploaded, I analyze the file, create an analysis result object and show analysis results basing on that object to the user. User reads analysis result and proceeds (clicks a button) or not. If he does than I take that analysis result object and perform an action (processing) based on it. I show the results of the processing to the user.

Depending on some query parameters (for sake of simplicity) the file should be processed by different classes. The processor needs analysis result specific for the concrete implementation of the analyzer (result of analyzing one file might inclde list of custom classes, result of different file analysis might contain other specific fields). The processor needs those implementation-specific details to do it's job.

I made an attempt to create general project but am stuck.

The magicId in the factory is a query parameter specifying which class should be used to parse/analyze the file.

Is there a way to create a working factory of classes, which all implement a genericly-typed interface?

abstract class AnalysisResult
{
}

interface IAnalyzer<TAnalysisResult>
    where TAnalysisResult : AnalysisResult
{
    TAnalysisResult PerformAction(string input);
}

class ConcreteAnalysisResult : AnalysisResult {
    public int SpecificProperty { get; set; }
}

class ConcreteAnalyzer : IAnalyzer<ConcreteAnalysisResult>
{
    public ConcreteAnalysisResult PerformAction(string input)
    {
        return new ConcreteAnalysisResult() { SpecificProperty = input.Length };
    }
}

class AnAnalyzerFactory
{
    IAnalyzer<AnalysisResult> CreateByMagicId(int magicId)
    {
        return new ConcreteAnalyzer();
    }
}

Upvotes: 1

Views: 284

Answers (2)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726599

Although you can fix your code by using type covariance, there would remain a place in your code where you dispatch based on the concrete type of the analyzer. This is suboptimal, because the use of generics hides something that needs to be "unhidden" later on (for example, to get to the SpecificProperty property).

I think that a better approach would be to invert the control on the recipient of the analysis results in a way similar to the visitor pattern, like this:

// The following removes generics from your code
abstract class AnalysisResult {
}

interface IAnalyzer {
    AnalysisResult PerformAction(string input);
}

class ConcreteAnalysisResult : AnalysisResult {
    public int SpecificProperty { get; set; }
}

class ConcreteAnalyzer : IAnalyzer {
    public AnalysisResult PerformAction(string input) {
        return new ConcreteAnalysisResult() { SpecificProperty = input.Length };
    }
}
// Here is the magic that lets you process results of specific types without generics
public static void main(string[] args) {
    var analyzer = new ConcreteAnalyzer();
    dynamic res = analyzer.PerformAction(input);
    ProcessResult(res); // This gets dispatched dynamically to the correct overload
}
private static void ProcessResult(ConcreteAnalysisResult cr) {
    Console.WriteLine(cr.SpecificProperty);
}
private static void ProcessResult(SomeOtherConcreteAnalysisResult cr) {
    Console.WriteLine(cr.AnoterhSpecificProperty);
}
private static void ProcessResult(AnalysisResult cr) {
    // Throw an error: unexpected result type
}

Note that the above code is free of static type casting, and it does not use generics, which became unnecessary after the refactoring.

Upvotes: 2

Mehmet Ataş
Mehmet Ataş

Reputation: 11549

Declare TAnalysisResult as covariant. For details you can check Covariance and Contravariance

interface IAnalyzer<out TAnalysisResult> // note out keyword
    where TAnalysisResult : AnalysisResult
{
    TAnalysisResult PerformAction(string input);
}

Upvotes: 4

Related Questions