수색…


통사론

  • DerivedClass 클래스 : BaseClass
  • DerivedClass 클래스 : BaseClass, IExampleInterface
  • 클래스 DerivedClass : BaseClass, IExampleInterface, IAnotherInterface

비고

클래스는 하나의 클래스에서 직접 상속받을 수 있지만 (대신 또는 동시에) 하나 이상의 인터페이스를 구현할 수 있습니다.

구조체는 인터페이스를 구현할 수 있지만 명시 적으로 모든 유형을 상속 할 수는 없습니다. System.ValueType 암시 적으로 상속하며 System.Object 에서 직접 상속합니다.

정적 클래스 인터페이스를 구현할 수 없습니다 .

기본 클래스에서 상속

코드 복제를 피하려면 일반 클래스의 공통 메소드 및 속성을 기본 클래스로 정의하십시오.

public class Animal 
{
    public string Name { get; set; }
    // Methods and attributes common to all animals
    public void Eat(Object dinner)
    {
        // ...
    }
    public void Stare()
    {
        // ...
    }
    public void Roll()
    {
        // ...
    }
}

일반적으로 Animal 을 나타내는 클래스가 있으므로 특정 동물의 특성을 나타내는 클래스를 정의 할 수 있습니다.

public class Cat : Animal
{
    public Cat() 
    {
        Name = "Cat";
    }
    // Methods for scratching furniture and ignoring owner
    public void Scratch(Object furniture)
    {
        // ...
    }
}

Cat 클래스는 정의에 명시 적으로 설명 된 메소드뿐만 아니라 일반 Animal 기본 클래스에 정의 된 모든 메소드에 액세스 할 수 있습니다. 어떤 동물 (그것이 고양이이든 아니든)은 먹거나, 찌르거나, 굴릴 수 있습니다. 그러나 동물도 고양이가 아니면 동물은 긁을 수 없습니다. 그런 다음 다른 동물을 묘사하는 다른 클래스를 정의 할 수 있습니다. (Gopher와 같은 방법으로 꽃밭과 Sloth를 파괴하는 방법은 전혀 없습니다.)

클래스에서 상속 받고 인터페이스를 구현

public class Animal 
{
    public string Name { get; set; }
}

public interface INoiseMaker
{
    string MakeNoise();
}

//Note that in C#, the base class name must come before the interface names
public class Cat : Animal, INoiseMaker
{
    public Cat() 
    {
        Name = "Cat";
    }

    public string MakeNoise()
    {
        return "Nyan";
    }
}

클래스에서 상속 받고 다중 인터페이스 구현하기

public class LivingBeing
{
    string Name { get; set; }
}

public interface IAnimal 
{
    bool HasHair { get; set; }
}

public interface INoiseMaker
{
    string MakeNoise();
}

//Note that in C#, the base class name must come before the interface names
public class Cat : LivingBeing, IAnimal, INoiseMaker
{
    public Cat() 
    {
        Name = "Cat";
        HasHair = true;
    }

    public bool HasHair { get; set; }

    public string Name { get; set; }

    public string MakeNoise()
    {
        return "Nyan";
    }
}

상속 테스트 및 탐색

interface BaseInterface {}
class BaseClass : BaseInterface {}

interface DerivedInterface {}
class DerivedClass : BaseClass, DerivedInterface {}

var baseInterfaceType = typeof(BaseInterface);
var derivedInterfaceType = typeof(DerivedInterface);
var baseType = typeof(BaseClass);
var derivedType = typeof(DerivedClass);

var baseInstance = new BaseClass();
var derivedInstance = new DerivedClass();  

Console.WriteLine(derivedInstance is DerivedClass); //True
Console.WriteLine(derivedInstance is DerivedInterface); //True
Console.WriteLine(derivedInstance is BaseClass); //True
Console.WriteLine(derivedInstance is BaseInterface); //True
Console.WriteLine(derivedInstance is object); //True

Console.WriteLine(derivedType.BaseType.Name);  //BaseClass
Console.WriteLine(baseType.BaseType.Name);  //Object
Console.WriteLine(typeof(object).BaseType);  //null

Console.WriteLine(baseType.IsInstanceOfType(derivedInstance));  //True
Console.WriteLine(derivedType.IsInstanceOfType(baseInstance));  //False

Console.WriteLine(
    string.Join(",", 
    derivedType.GetInterfaces().Select(t => t.Name).ToArray()));
//BaseInterface,DerivedInterface
    
Console.WriteLine(baseInterfaceType.IsAssignableFrom(derivedType)); //True
Console.WriteLine(derivedInterfaceType.IsAssignableFrom(derivedType)); //True
Console.WriteLine(derivedInterfaceType.IsAssignableFrom(baseType)); //False

추상 기본 클래스 확장

구현을위한 계약으로 설명 할 수있는 인터페이스와 달리 추상 클래스는 확장을위한 계약의 역할을합니다.

추상 클래스는 인스턴스화 할 수 없으므로 확장되어야하며 결과 클래스 (또는 파생 클래스)는 인스턴스화 될 수 있습니다.

추상 클래스는 일반적인 구현을 제공하는 데 사용됩니다.

public abstract class Car
{
    public void HonkHorn() {
        // Implementation of horn being honked
    }
}

public class Mustang : Car
{
    // Simply by extending the abstract class Car, the Mustang can HonkHorn()
    // If Car were an interface, the HonkHorn method would need to be included
    // in every class that implemented it.
}

위의 예는 Car를 확장하는 모든 클래스가 구현과 함께 HonkHorn 메소드를 자동으로 수신하는 방법을 보여줍니다. 즉, 새 자동차를 만드는 모든 개발자는 경적을 울리는 방법에 대해 걱정할 필요가 없습니다.

서브 클래스의 생성자

기본 클래스의 하위 클래스를 만들 때 하위 클래스 생성자의 매개 변수 : base 를 사용하여 기본 클래스를 구성 할 수 있습니다.

class Instrument
{
    string type;
    bool clean;

    public Instrument (string type, bool clean)
    {
        this.type = type;
        this.clean = clean;
    }
}

class Trumpet : Instrument
{
    bool oiled;

    public Trumpet(string type, bool clean, bool oiled) : base(type, clean)
    {
        this.oiled = oiled;
    }
}

계승. 생성자의 호출 순서

우리는 어린이 클래스를 가진 Animal 클래스를 가지고 있다고 생각합니다. Dog

class Animal
{
    public Animal()
    {
        Console.WriteLine("In Animal's constructor");
    }
}

class Dog : Animal
{
    public Dog()
    {
        Console.WriteLine("In Dog's constructor");
    }
}

기본적으로 모든 클래스는 암시 적으로 Object 클래스를 상속합니다.

위의 코드와 같습니다.

class Animal : Object
{
    public Animal()
    {
        Console.WriteLine("In Animal's constructor");
    }
}

Dog 클래스의 인스턴스를 만들 때 부모 클래스의 다른 생성자에 대한 명시 적 호출이 없으면 매개 변수가없는 기본 클래스의 기본 생성자가 호출됩니다 . 우리의 경우, 먼저 Object's 생성자, Animal's 끝, Dog's 생성자라고합니다.

public class Program
{
    public static void Main()
    {
        Dog dog = new Dog();
    }
}

출력은

동물의 생성자에서
개 생성자에서

데모보기

부모의 생성자를 명시 적으로 호출합니다.

위의 예제에서 Dog 클래스 생성자는 Animal 클래스의 기본 생성자를 호출합니다. 원하는 경우 생성자를 호출해야하는지 지정할 수 있습니다. 부모 클래스에 정의 된 모든 생성자를 호출 할 수 있습니다.

이 두 클래스가 있다고 가정 해보십시오.

class Animal
{
    protected string name;

    public Animal()
    {
        Console.WriteLine("Animal's default constructor");
    }    

    public Animal(string name)
    {
        this.name = name;
        Console.WriteLine("Animal's constructor with 1 parameter");
        Console.WriteLine(this.name);
    }
}

class Dog : Animal
{
    public Dog() : base()
    {
        Console.WriteLine("Dog's default constructor");
    }  

    public Dog(string name) : base(name)
    {
        Console.WriteLine("Dog's constructor with 1 parameter");
        Console.WriteLine(this.name);
    }
}

여기서 뭐하는거야?

각 클래스에는 2 개의 생성자가 있습니다.

base 는 무엇을 의미합니까?

base 는 부모 클래스에 대한 참조입니다. 이 경우 Dog 클래스의 인스턴스를 만들 때

Dog dog = new Dog();

런타임은 매개 변수없는 생성자 인 Dog() 먼저 호출합니다. 그러나 시신은 즉시 작동하지 않습니다. 생성자의 괄호 뒤에 다음과 같은 호출이 있습니다. base() . 기본 Dog 생성자를 호출하면 부모의 기본 생성자가 차례로 호출됩니다. 부모 생성자가 실행 된 후 Dog() 생성자 본문이 반환 된 다음 마지막으로 실행됩니다.

출력은 다음과 같습니다.

동물의 기본 생성자
개 기본 생성자

데모보기

이제 Dog's 생성자를 매개 변수로 호출하면 어떻게 될까요?

Dog dog = new Dog("Rex");

비공개가 아닌 부모 클래스의 멤버는 자식 클래스에 상속됩니다. 즉, Dog 에는 name 필드도 있습니다.
이 경우 우리는 생성자에게 인수를 넘겼습니다. 자신의 차례가되면 (자), name 필드를 초기화 하는 파라미터 를 사용해, 인수를 친 클래스의 생성자에게 건네줍니다.

출력은

Animal's constructor with 1 parameter
Rex
Dog's constructor with 1 parameter
Rex

개요:

모든 객체 생성은 기본 클래스에서 시작됩니다. 상속에서는 계층 구조에있는 클래스가 연결됩니다. 모든 클래스가 Object 에서 파생되므로 모든 객체가 만들어 질 때 호출되는 첫 번째 생성자는 Object 클래스 생성자입니다. 그런 다음 체인의 다음 생성자가 호출되고 모든 객체가 호출 된 후에 만 ​​객체가 만들어집니다.

기본 키워드

  1. base 키워드는 파생 클래스 내에서 기본 클래스의 멤버에 액세스하는 데 사용됩니다.
  2. 다른 메소드에 의해 오버라이드 된 기본 클래스의 메소드를 호출하십시오. 파생 클래스의 인스턴스를 만들 때 호출해야하는 기본 클래스 생성자를 지정합니다.

상속 메소드

메서드를 상속 할 수있는 몇 가지 방법이 있습니다.

public abstract class Car
{
    public void HonkHorn() {
        // Implementation of horn being honked
    }

    // virtual methods CAN be overridden in derived classes
    public virtual void ChangeGear() {
        // Implementation of gears being changed
    }

    // abstract methods MUST be overridden in derived classes
    public abstract void Accelerate();
}

public class Mustang : Car
{
    // Before any code is added to the Mustang class, it already contains 
    // implementations of HonkHorn and ChangeGear.

    // In order to compile, it must be given an implementation of Accelerate,
    // this is done using the override keyword
    public override void Accelerate() {
        // Implementation of Mustang accelerating
    }

    // If the Mustang changes gears differently to the implementation in Car
    // this can be overridden using the same override keyword as above
    public override void ChangeGear() {
        // Implementation of Mustang changing gears
    }
}

상속 안티 패턴

부적절한 상속

FooBar 클래스 두 클래스가 있다고 가정 해 Foo . FooDo1Do2 두 가지 기능을 제공합니다. Bar 사용할 필요가 Do1 에서 Foo 하지만 필요하지 않습니다 Do2 또는 요구가 그와 동일 기능 Do2 하지만 완전히 다른 무언가를.

나쁜 방법 : Foo 가상에서 Do2() 를 만들고 Bar 에서 무시하거나 Do2()Bar 에서 throw Exceptionthrow Exception

public class Bar : Foo
{
    public override void Do2()
    {
        //Does something completely different that you would expect Foo to do
        //or simply throws new Exception 
    }
}

좋은 방법

Foo 에서 Do1() 을 꺼내 새로운 클래스 Baz 넣은 다음 Baz 에서 FooBar 를 상속하고 Do2() 별도로 구현합니다.

public class Baz
{
    public void Do1()
    {
        // magic
    }
}

public class Foo : Baz
{
    public void Do2()
    {
        // foo way
    }
}

public class Bar : Baz
{
    public void Do2()
    {
        // bar way or not have Do2 at all
    }
}

이제 첫 번째 예제가 좋지 않고 두 번째 예제가 좋은 이유는 다음과 같습니다. 개발자 nr2가 Foo 에서 변경 작업을 수행해야 할 때 BarFoo 분리 할 수 ​​없으므로 Bar 구현을 중단 할 가능성이 있습니다. 후자의 예제 FooBar commonalty가 Baz 로 옮겨 졌을 때 서로 영향을 미치지 않습니다.

재귀 유형 지정이있는 기본 클래스

재귀 유형 지정자를 사용하는 일반 기본 클래스의 한 번 정의. 각 노드에는 하나의 상위 노드와 여러 하위 노드가 있습니다.

/// <summary>
/// Generic base class for a tree structure
/// </summary>
/// <typeparam name="T">The node type of the tree</typeparam>
public abstract class Tree<T> where T : Tree<T>
{
    /// <summary>
    /// Constructor sets the parent node and adds this node to the parent's child nodes
    /// </summary>
    /// <param name="parent">The parent node or null if a root</param>
    protected Tree(T parent)
    {
        this.Parent=parent;
        this.Children=new List<T>();
        if(parent!=null)
        {
            parent.Children.Add(this as T);
        }
    }
    public T Parent { get; private set; }
    public List<T> Children { get; private set; }
    public bool IsRoot { get { return Parent==null; } }
    public bool IsLeaf { get { return Children.Count==0; } }
    /// <summary>
    /// Returns the number of hops to the root object
    /// </summary>
    public int Level { get { return IsRoot ? 0 : Parent.Level+1; } }
}

객체의 트리 계층 구조를 정의해야 할 때마다 위의 내용을 다시 사용할 수 있습니다. 트리의 노드 객체는 다음을 사용하여 기본 클래스에서 상속해야합니다.

public class MyNode : Tree<MyNode>
{
    // stuff
}

각 노드 클래스는 계층 구조에서의 위치, 부모 객체가 무엇인지뿐만 아니라 자식 객체가 무엇인지를 알고 있습니다. 여러 가지 내장 된 유형은 Control 또는 XmlElement 와 같은 트리 구조를 사용하며 위의 Tree<T> 는 코드에서 모든 유형의 기본 클래스로 사용할 수 있습니다.


예를 들어, 전체 체중이 모든 어린이의 체중에서 계산되는 부분 계층 구조를 만들려면 다음을 수행하십시오.

public class Part : Tree<Part>
{
    public static readonly Part Empty = new Part(null) { Weight=0 };
    public Part(Part parent) : base(parent) { }
    public Part Add(float weight)
    {
        return new Part(this) { Weight=weight };
    }
    public float Weight { get; set; }

    public float TotalWeight { get { return Weight+Children.Sum((part) => part.TotalWeight); } }
}

다음과 같이 사용된다.

// [Q:2.5] -- [P:4.2] -- [R:0.4]
//    \
//      - [Z:0.8]
var Q = Part.Empty.Add(2.5f);
var P = Q.Add(4.2f);
var R = P.Add(0.4f);
var Z = Q.Add(0.9f);

// 2.5+(4.2+0.4)+0.9 = 8.0
float weight = Q.TotalWeight;

다른 예는 상대 좌표 프레임의 정의에 있습니다. 이 경우 좌표 프레임의 실제 위치는 모든 상위 좌표 프레임의 위치에 따라 달라집니다.

public class RelativeCoordinate : Tree<RelativeCoordinate>
{
    public static readonly RelativeCoordinate Start = new RelativeCoordinate(null, PointF.Empty) { };
    public RelativeCoordinate(RelativeCoordinate parent, PointF local_position)
        : base(parent)
    {
        this.LocalPosition=local_position;
    }
    public PointF LocalPosition { get; set; }
    public PointF GlobalPosition
    {
        get
        {
            if(IsRoot) return LocalPosition;
            var parent_pos = Parent.GlobalPosition;
            return new PointF(parent_pos.X+LocalPosition.X, parent_pos.Y+LocalPosition.Y);
        }
    }
    public float TotalDistance
    {
        get
        {
            float dist = (float)Math.Sqrt(LocalPosition.X*LocalPosition.X+LocalPosition.Y*LocalPosition.Y);
            return IsRoot ? dist : Parent.TotalDistance+dist;
        }
    }
    public RelativeCoordinate Add(PointF local_position)
    {
        return new RelativeCoordinate(this, local_position);
    }
    public RelativeCoordinate Add(float x, float y)
    {
        return Add(new PointF(x, y));
    }
}

다음과 같이 사용된다.

// Define the following coordinate system hierarchy
//
// o--> [A1] --+--> [B1] -----> [C1]
//             |     
//             +--> [B2] --+--> [C2]
//                         |
//                         +--> [C3]

var A1 = RelativeCoordinate.Start;
var B1 = A1.Add(100, 20);
var B2 = A1.Add(160, 10);

var C1 = B1.Add(120, -40);
var C2 = B2.Add(80, -20);
var C3 = B2.Add(60, -30);

double dist1 = C1.TotalDistance;


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow