Реализация явных членов интерфейса с обобщениями - PullRequest
2 голосов
/ 28 сентября 2011

У меня есть неуниверсальный интерфейс к общей группе классов. У меня есть метод, который использует ковариацию для возврата строго типизированного экземпляра в классе, полученном из абстрактной реализации неуниверсальной группы классов.

Так что в основном с этой настройкой у меня есть несколько классов, например ActualThing1 и ActualThing2. Эти классы имеют метод Clone, который возвращает строго типизированный клон самого себя.

Ниже показано, как я структурировал интерфейс и классы. Я не уверен, что это правильно. Обычно у меня вообще не было бы не универсального абстрактного класса. Но это необходимо, потому что есть ситуация, когда я должен что-то делать с дочерними объектами, ссылаясь на внутренний метод _SomethingNeededInternally. Это не может быть сделано с помощью интерфейса, поскольку он защищен, и при этом это не может быть сделано из общего класса, так как дочерние элементы могут быть не того же типа (они могут быть другого типа, производного от интерфейса). Следовательно, неуниверсальный базовый класс Something.

Это работает, но для меня не совсем понятна явная реализация ISomething.Clone, которая необходима в моем абстрактном Something классе. Он должен быть там, но не должен быть реализован, так как я хочу, чтобы эта реализация была перенесена в общий класс, производный от него. Но нет такого синтаксиса как abstract ISomething ISomething.Clone().

Но этот код (где исключение - throw) не может быть выполнен, не так ли, поскольку у меня нет каких-либо неуниверсальных реализаций этого объекта?

Думаю, мне интересно, есть ли лучший способ сделать это, потому что это кажется неправильным. То есть я зависел от того факта, что неуниверсальный класс никогда не создается из интерфейса, что пахнет забавно.

public interface ISomething
{
    // ...
    ISomething Clone();
}

public abstract class Something: ISomething 
{
    ISomething ISomething.Clone()
    {
        throw new Exception("This should not be happening");
    }
    protected int _SomethingNeededInternally;
}

public abstract class Something<T>: Something, ISomething where T: ISomething, new()
{
    public abstract T Clone();

    ISomething ISomething.Clone()
    {
        return Clone();
    }
}

public interface IActualThing {} // this could be any interface

public class ActualThing: Something<ActualThing>, IActualThing
{
    public override ActualThing Clone() 
    {
        return new ActualThing(); // do cloning here
    }
}

Ответы [ 5 ]

3 голосов
/ 28 сентября 2011

Будет ли добавление необходимого метода в качестве реализации Clone для вас? (Я все еще не совсем уверен, в чем вся ваша проблема, это просто решит синтаксис для реализации абстрактного явного интерфейса.)

public abstract class Something: ISomething  
{ 
    ISomething ISomething.Clone() 
    { 
        return CloneYouMust();
    } 
    abstract ISomething CloneYouMust();
}
2 голосов
/ 28 сентября 2011

Я думаю, что это может работать лучше:

public interface ISomething
{
    ISomething Clone();
}

public abstract class Something : ISomething 
{
    protected abstract ISomething CloneInternal();

    ISomething ISomething.Clone()
    {
        return CloneInternal();
    }
}

public abstract class Something<T>: Something, ISomething where T: ISomething, new()
{
    protected override ISomething CloneInternal()
    {
        return Clone();
    }

    public abstract T Clone();
}

public class ActualThing: Something<ActualThing>, IActualThing
{
    public override ActualThing Clone() 
    {
    }
}
0 голосов
/ 08 октября 2011

Я бы предложил вам определить и реализовать интерфейсы ISelf , содержащие одно свойство Self типа T и ICloneable., производный от ISelf , с методом Clone () типа T. Тогда класс, который реализует ICloneable , может реализовать его с помощью метода, возвращающего ItsOwnType;этот метод должен затем вызывать виртуальный метод InternalClone для выполнения реальной работы (в противном случае вызов Clone для переменной базового типа может в конечном итоге вызвать метод Clone базового типа, а не производный метод).

0 голосов
/ 28 сентября 2011

Ваша жалоба на abstract ISomething ISomething.Clone действительна, но в интерфейсах ничего не говорится о том, что вы должны явно объявить их.Приведенный ниже код показывает рабочий пример и устраняет требование универсального класса.

interface Igloo
{
    Igloo Clone();
}

abstract class Alpha : Igloo
{
    public abstract Igloo Clone();
}

class Bravo : Alpha
{
    public override Igloo Clone()
    {
        /* implement */
        return null;
    }
}
0 голосов
/ 28 сентября 2011

Прежде всего, я думаю, что наличие двух методов Clone () внутри Something является избыточным.Поскольку вы уже ограничили T как ISomething и new (), почему бы не сделать это:

public abstract class Something<T>: Something, ISomething 
       where T: ISomething, new() 
{     
    public override ISomething Clone()    
    {
        return new T().Clone(); 

        //alternatively may have to use Activator.CreateInstance() here
    }     
} 

Во-вторых, я не совсем понимаю, должен ли _SomethingNeededInternally быть абстрактным или виртуальным, но этолибо не помечен.Моя интерпретация заключается в том, что он должен быть виртуальным, но, возможно, вам придется настроить его.В любом случае, вот общая схема, которая, кажется, хорошо работает для меня и имеет смысл.Обратите внимание, что я смог сделать метод Clone () абстрактным в абстрактном классе 'Something':

public interface IActualThing
{
}

public interface ISomething 
{    
    ISomething Clone(); 
}  

public abstract class Something: ISomething  
{
    public abstract ISomething Clone();

    protected virtual void _SomethingNeededInternally()
    {
        throw new NotImplementedException(); 
    }
}  

public abstract class Something<T>: Something, ISomething where T: ISomething, new()
{    
    public override ISomething Clone()    
    {
        return new T().Clone(); 
    } 
}  

public class ActualThing: Something<ActualThing>, IActualThing 
{     
    public override ISomething Clone()      
    {
        throw new NotImplementedException(); 
    }
} 

Также обратите внимание, что вам необходимо пометить возвращаемый тип как ISomething в конечном классе ActualThing.Конечно, вы бы возвращали ActualThing в реальности, но, тем не менее, метод должен соответствовать интерфейсу.

...