Domanda

Apologies if this is a commonly asked question, but I have spend quite a while googling for the answer and could not find definitive answer.

I have an abstract class that contains fields that are (definitions of) other abstract classes . Those are then used for concrete implementations of properties. However when I want to initialize implementation of that abstract class I would like those fields to be populated with particular implementations of the fields. This actually sounds confusing even for myself, so here is the example.

// My main abstract class
public abstract class Log
{
    public virtual bool AppendLog
    {
        set { _logWriter.Append = value; }
    }

    internal LogWriter _logWriter; //LogWriter is another abstract class

    public abstract void AddEntry(string input);
}

// Implementation of abstract class
public class SyncLog : Log
{
    public SyncLog
    {
        // Now I want to initialize LogWriter abstract class in parent with
        // It's actual implementation SyncLogWriter : LogWriter
        _logWriter = new SyncLogWriter(); 
    }

    public override void AddEntry(string input)
    {
        content.AddEntry(input);
        _logWriter.Write("Hello");
    }
}

While this technically complies and even works, there is a problem. When using the _logWriter I can only access the methods and properties declared in abstract class LogWriter, but not ones that are additionally implemented in it's child (SyncLogWriter). While technically it makes sense I wonder if there is any way to do the similar approach, but making all the additional stuff available in SyncLogWriter be available?

È stato utile?

Soluzione

Use an additional member _syncLogWriter on the inherited class SyncLog. Also it is even better to initialize your _logWriter member for the base class through the constructor and make it private.

// My main abstract class
public abstract class Log
{
    protected Log(LogWriter logWriter)
    {
        _logWriter = logWriter;
    }

    public virtual bool AppendLog
    {
        set { _logWriter.Append = value; }
    }

    private LogWriter _logWriter; //LogWriter is another abstract class

    public abstract void AddEntry(string input);
}

// Implementation of abstract class
public class SyncLog : Log
{
    private SyncLogWriter _syncLogWriter

    public SyncLog() : this(new SyncLogWriter()) { }

    private SyncLog(SyncLogWriter logWriter) : base(logWriter)
    {
        _syncLogWriter = logWriter; 
    }

    public override void AddEntry(string input)
    {
        content.AddEntry(input);
        _syncLogWriter.Write("Hello");
    }
}

Altri suggerimenti

You can create a private readonly property of type SyncLogWriter which actually points to _logWriter.

public class SyncLog : Log
{
    public SyncLog
    {
        _logWriter = new SyncLogWriter(); 
    }

    private SyncLogWriter LogWriter
    {
        get { return (SyncLogWriter)_logWriter; }
    }

    public override void AddEntry(string input)
    {
        content.AddEntry(input);
        _logWriter.Write("Hello");
    }

    private void DoSomething()
    {
         LogWriter.SomeSyncLogWriterMethod();
    }
}

To my mind, a cleaner solution than the other two answers would be to use generics, as so:

public abstract class Log<TLogWriter> where TLogWriter : LogWriter
{
    public virtual bool AppendLog
    {
        set { _logWriter.Append = value; }
    }

    internal TLogWriter _logWriter;

    public abstract void AddEntry(string input);
}

public class SyncLog : Log<SyncLogWriter>
{
    public SyncLog
    {
        _logWriter = new SyncLogWriter(); 
    }

    public override void AddEntry(string input)
    {
        content.AddEntry(input);
        _logWriter.Write("Hello");
    }
}

This removes the need for duplicating fields, or casting

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top