C # наследование полей - PullRequest
       19

C # наследование полей

4 голосов
/ 30 марта 2010

Это, вероятно, глупый вопрос, но здесь он идет. Представьте, что у вас есть следующие классы:

public class C
{
}

public class D : C
{
     //A subclass of C
}

public class A
{
    C argument;     
}

Теперь я хочу иметь класс B, который наследуется от A. Этот класс, очевидно, будет наследовать поле «аргумент», но я хочу, чтобы поле «аргумент» в B было типа D, а не C Так как D наследует от C, это не должно создавать никаких проблем. Итак, как бы добиться этого в C #?

Ответы [ 8 ]

10 голосов
/ 30 марта 2010

Как отмечает Джаред, это невозможно. Однако интересно подумать, почему бы и нет.

Предположим, что поле является частным. Тогда к нему нельзя получить доступ в производном классе B. (Предполагая, что B не является вложенным типом A.)

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

class B : A 
{
    override public D argument; // "overrides" A.argument
}

class E : C {  }

class F
{
    public static void M(A a)
    { a.argument = new E(); }
}

... 
F.M(new B());

И эй, ты только что разбил среду выполнения. Вы просто записали объект типа E в поле, которое может хранить только тип D. (Мы могли бы создать аналогичные сценарии сбоя для других конфигураций, таких как защищенные поля и т. Д.)

Теперь, вы можете сказать, что ж, вместо этого позвольте мне сделать свойство только для чтения. Что я могу виртуализировать:

class A 
{
    public virtual C Argument { get; }
}
class B : A
{
    public override D Argument { ... }
}

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

Эта функция называется "ковариацией возвращаемого типа", и она также не поддерживается в C #. См. Почему понятия «ковариантность» и «контравариантность» применимы при реализации методов интерфейса? .

Пожалуйста, объясните, что вы действительно пытаетесь сделать, а не как вы пытаетесь это сделать. Возможно, есть лучший способ.

3 голосов
/ 30 марта 2010

Вы можете достичь чего-то подобного, где A является универсальным типом с параметром типа, ограниченным подклассами C: -

public class C
{
}

public class D : C
{
}

public class A<T> where T:C
{
    public T Argument { get; set; }
}

public class B : A<D>
{
}

Имейте в виду, что вы не должны открывать открытые (или защищенные) поля, поэтому я изменил их на свойства.

2 голосов
/ 30 марта 2010

Это не поддерживается в C #.Вы не можете изменить тип существующего поля или свойства или метода в этом отношении, производным от данного типа.

1 голос
/ 30 марта 2010

Если я полностью не скучаю по лодке, я думаю, вы просите что-то вроде этого

class A
{
    public virtual C Argument { get; set; }
}

class B : A
{
    D argument = null;
    public override C Argument
    {
        get
        {
            return argument;
        }
        set
        {
            if (value is D)
            {
                argument = (D)value;
            }
            else
            {
                throw new Exception();
            }
        }
    }
}

class C
{
}

class D : C
{
}

...

static void Main()
{
    B b = new B();
    D arg = new D();
    b.Argument = arg;
}
1 голос
/ 30 марта 2010

Чтобы сделать его безопасным, вам придется что-то делать с генериками. В противном случае просто выполните приведение.

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

0 голосов
/ 30 марта 2010

Что вам нужно, так это языковая функция, называемая «ковариация возвращаемого типа», которую C # не поддерживает.

У вас есть еще один вариант: вы можете «затенять» поле, а не наследовать его. Однако это очень опасно и почти повсеместно является плохой идеей, потому что все, что назначено объекту как B.argument, не будет доступно, если оно будет выгружено и указано как A.argument.

Если вы находитесь на C # 4.0, вы можете получить некоторые аспекты того, что вы пытаетесь, используя универсальный ковариантный интерфейс:

public class C
{
    public int foo;
}

public class D : C
{
}

public class A : ITest<C>
{
    public C foo { get; private set; }
}

public class B : ITest<D>
{
    public D foo { get; private set; }
}

public interface ITest<out T> where T : C
{
    T foo { get; }
}

static class Program
{
    public static void Covariance(ITest<C> test)
    {

    }

    static void Main()
    {
        A myCVar = new A();
        B myDVar = new B();
        Covariance(myDVar);
    }
}

Обратите внимание, что функция Covariance будет принимать B (то есть ITest<D>, а не ITest<C>), поскольку интерфейс ITest пометил параметр T ключевым словом out , Это говорит компилятору, что этот параметр типа будет только использоваться в выходных операциях, поэтому при использовании этого интерфейса безопасно заменить любой производный класс T.

0 голосов
/ 30 марта 2010

Свойство / метод аргумента в B может возвращать D, так как D наследуется от C, но, как писал JaredPar, его нужно ввести как C.

0 голосов
/ 30 марта 2010

это немного простой вопрос. Обычно это плохая практика - скрывать аргумент базового класса с одним и тем же именем в производном классе.

публичный класс B: A { D аргумент; }

Вы также можете сделать это ...

публичный класс B: A { общедоступный B () { аргумент = новый D (); } }

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...