4

如何在 C# 中声明一个应该被派生类覆盖(或可覆盖)的方法——甚至可能在你的程序集之外——但它应该只能从实际类中调用?

(即像 C++ 中的私有虚函数)

[编辑]正是我想要的:“这是一种修改我的行为的方法,但仍然不允许您直接调用此函数(因为调用它需要只有我的基类才能执行的神秘调用)”
private virtual

所以澄清一下:在 C# 中最好的表达是什么?

4

8 回答 8

3

当您说它应该只能在“实际类中”调用时,您是指基类还是派生类?这些都不是单独可行的。最接近的是使用受保护的方法,这意味着它可以从声明类、派生类和任何进一步派生的类中调用。

于 2009-01-05T22:44:46.800 回答
2

私有成员对子类不可见。我认为受保护的虚拟会按照您想要的方式执行?

更新:

这里更详细地解释了您可以在 C# 中使用继承和覆盖函数执行哪些操作。我尝试使用一个有意义的示例,但认为它理解它的类设计很差,我永远不会推荐以这种方式实现描述的类。但是,我希望这可能会为您提供一种途径,以一种可以接受的方式解决您的原始问题。没有办法阻止一个具体的类调用它的任何成员,但是如果你的结构无论如何都是这样的,也许它不是问题。

public abstract class Animal
{
    public void DisplayAttributes()
    {
        Console.WriteLine(Header());
        Console.WriteLine("Name: " + Name());
        Console.WriteLine("Legs: " + Legs());
        Console.WriteLine();
    }

    protected virtual int Legs()
    {
        return 4;
    }

    private string Header()
    {
        return "Displaying Animal Attributes";
    }

    protected abstract string Name();
}

public class Bird : Animal
{
    protected override string Name()
    {
        return "Bird";
    }

    protected override int Legs()
    {
        return 2;
    }
}

public class Zebra : Animal
{
    protected override string Name()
    {
        return "Zebra";
    }
}

public class Fish : Animal
{
    protected override string Name()
    {
        return "Fish";
    }

    protected override int Legs()
    {
        return 0;
    }

    private string Header()
    {
        return "Displaying Fish Attributes";
    }

    protected virtual int Gils()
    {
        return 2;
    }

    public new void DisplayAttributes()
    {
        Console.WriteLine(Header());
        Console.WriteLine("Name: " + Name());
        Console.WriteLine("Gils: " + Gils());
        Console.WriteLine();
    }
}

class Program
{
    static void Main(string[] args)
    {
        Bird bird = new Bird();
        bird.DisplayAttributes();
        //Displaying Animal Attributes
        //Name: Bird
        //Legs: 2

        Zebra zebra = new Zebra();
        zebra.DisplayAttributes();
        //Displaying Animal Attributes
        //Name: Zebra
        //Legs: 4


        Fish fish = new Fish();
        fish.DisplayAttributes();
        //Displaying Fish Attributes
        //Name: Fish
        //Gils: 2

        List<Animal> animalCollection = new List<Animal>();
        animalCollection.Add(bird);
        animalCollection.Add(zebra);
        animalCollection.Add(fish);

        foreach (Animal animal in animalCollection)
        {
            animal.DisplayAttributes();
            //Displaying Animal Attributes
            //Name: Bird
            //Legs: 2

            //Displaying Animal Attributes
            //Name: Zebra
            //Legs: 4

            //Displaying Animal Attributes
            //Name: Fish
            //Legs: 0
            //*Note the difference here
            //Inheritted member cannot override the
            //base class functionality of a non-virtual member
        }
    }
}

在这个例子中,Bird、Zebra 和 Fish 都可以调用它们的 Name 和 Legs 方法,但是在这个例子的上下文中,这样做不一定有用。此外,如 Fish 所示,DisplayAttributes() 可以针对具体派生类的实例进行修改;但是当您查看动物时,就像在 foreach 循环中一样,您将获得基类 DisplayAttributes 行为,而不管动物的实际类型如何。我希望这可能有助于提供您想要复制的功能类型。

于 2009-01-05T22:41:39.443 回答
2

C# 比 C++ 为“私有”提供了更强的保证。在 C++ 中,您确实可以覆盖私有虚拟方法。但这意味着基类中的代码可以执行派生类中的代码。打破了私有方法是真正私有的并且只能由同一类中的方法调用的承诺。

在这里没有帮助的是 C++ 不需要重复 virtual 关键字。导致像这样一个难以逆向工程的谜团:

#include "stdafx.h"
#include <iostream>

class Base {
private:
    virtual void Method() = 0;
public:
    void Test() {
        Method();
    }
};
class Derived : public Base {
private:
    void Method() { std::cout << "Who the heck called me?"; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Base* p = new Derived;
    p->Test();
}

我同意私有继承可能发挥作用。C# 语言设计者说不!尽管。

于 2009-01-05T23:13:27.710 回答
2

这是 vboctor 已经提到的示例:

public class Base
{
    private Func<Base, int> func;
    protected void SetFunc(Func<Base, int> func)
    {
        this.func = func;
    }

    private void CallFunc()
    {
        if (func != null)
        {
            var i = func(this);
        }
    }
}

public class Sub : Base
{
    private void DoFuncyStuff()
    {
         this.SetFunc(b => 42);
    }
}
于 2009-03-27T01:06:06.750 回答
1

您是否考虑过使用委托来执行此操作?您可以允许派生类通过某些受保护的属性设置委托或将其传递给您的构造函数。您还可以将委托默认为您的内部实现,这是您的基类上的私有方法。

于 2009-01-06T00:57:48.700 回答
0

为什么你需要它是私有的?保护应该足够了,在这里。您要求子类作者编写他们无法调用的代码。这有什么作用?无论如何,他们都可以使用该代码。

于 2009-01-05T22:42:13.250 回答
0

当我阅读您的问题时,您可能意味着两件事。

首先,如果您想要 A 类中的一个函数,该函数可以在子类 B 中被覆盖但对任何外部类都不可见:

public class ClassA
{
  protected virtual ReturnType FunctionName(...) { ... }
}

public class ClassB
{
  protected override ReturnType FunctionName(...) { ... }
}

其次,如果要强制实现类定义函数:

public abstract class ClassA
{
  protected abstract ReturnType FunctionName(...);
}

public class ClassB
{
  protected override ReturnType FunctionName(...) { ... }
}

如果您只是深入研究与 C# 相关的另一个概念,您可能会看到部分类。这是在编译时组合两个源文件以创建一个类的想法,两者都来自同一个程序集:

文件 1:

public partial class ClassA
{
    private ReturnType FunctionName(...);
}  

文件 2:

public partial class ClassA
{
  //actual implimentation
  private ReturnType FunctionName(...){ ... }; 
}

除了处理设计生成的文件(如 Linq2Sql 文件、EDM 或 WinForms 等)外,分部并未广泛使用。

于 2009-01-05T22:54:24.940 回答
0

猜猜这不会像你想要的那样工作,但让我为你画一些伪代码:

public interface BaseClassFunction {
    void PleaseCallMe();
}

public class BaseClass {
    private BaseClassFunction fn;
    public BaseClass(BaseClassFunction fn) {
        this.fn = fn;
    }
    private CallMe() {
        fn.PleaseCallMe();
    }
    public PublicCallMe() {
        CallMe();
    }
}

private class DerivedClassFunction : BaseClassFunction {
    void PleaseCallMe() { ... do something important ... }
}

public class DerivedClassFunction {
    public DerivedClassFunction() : BaseClass(new DerivedClassFunction()) {
    }
}
于 2009-01-05T23:07:30.157 回答