C++和C#调用不同目录其他文件中的函数或方法

在C++中

头文件

cpp 中调用其他路径或文件中的方法,是通过头文件来进行标识的,例如现在有这么一个项目结构:

cppStudy/
├── main.cpp
├── src/
│   └── test.cpp
└── include/
    └── test.h

现在要在 main.cpp 中调用 test.cpp 文件中的 testTest() 方法,可以使用如下的步骤:

test.cpp 中,引入 test.h 头文件,和写好 testTest() 方法:

#include <iostream>
#include "../include/test.h"

using namespace std;

int testTest() {
    int my_array[5] = { 1, 2, 3, 4, 5 };
    for (int& x : my_array) {    // 或者int &x : my_array
        x *= 2;
        cout << x << endl;
    }
    return 0;
}

test.h 文件中,声明要调用的方法:

#ifndef TEST_H  // 防止头文件重复包含
#define TEST_H

// 声明函数
int testTest();

#endif  // TEST_H #pragma once

main.cpp 中进行包含和调用:

// main.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include "./include/test.h"

using namespace std;

int main() {

    testTest();

}

C++ 的这种头文件方法,类似于一个中介,test.h 中介现在根据 testTest() 这个需求先去 test.cpp 中查找,因为 test.cpp 包含了这个头文件,也就相当于中介有了这个源文件的联系方式,main.cpp 中也包含了这个头文件,也有中介的联系方式,那么就可以通过中介获取到 test.cpp 中的 testTest() 主体。

命名空间

本质上,使用命名空间来调用不同源文件中的方法,也必须要通过头文件的“转发”,例如:

在头文件 test.h 中定义命名空间和函数:

#ifndef TEST_H
#define TEST_H

namespace MyNamespace {
    void testTest();
}

#endif // TEST_H

在源文件 test.cpp 中实现命名空间中的函数:

#include "../include/test.h"
#include <iostream>

namespace MyNamespace {
    void testTest() {
        std::cout << "Hello from MyNamespace!" << std::endl;
    }
}

main.cpp 源文件中调用命名空间中的函数:

// main.cpp
#include "../include/test.h"

int main() {
    MyNamespace::testTest();  // 调用命名空间中的函数
    return 0;
}

在Csharp中

和 C++ 不同的是,在 C# 中,没有类似于 C++ 的头文件( .h.hpp )的概念 。C# 使用 命名空间(Namespace)类(Class) 来组织代码,并通过 项目引用using 指令 来访问其他文件中的代码,例如有如下一个项目结构:

CsharpStudy/
├── MainProgram.cs
├── ProgramSrc1/
│   └── Test1.cs
└── ProgramSrc2/
    └── Test2.cs

通常情况下,推荐源文件的文件夹和命名以 大写 开头。

Test1.cs 文件中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CsharpStudy.ProgramSrc1
{
    public class Test1
    {
        public static void Testtest1()
        {
            Console.WriteLine("Hello World");
            // Console.ReadKey();
        }
    }
}

Test2.cs 文件中:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CsharpStudy.ProgramSrc2
{
    public class Test2
    {
        public static void Testtest2()
        {
            Console.WriteLine("Hello World");
            // Console.ReadKey();
        }
    }
}

MainProgram.cs 文件中进行调用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// 命名空间
namespace CsharpStudyTest
{
    // 类型
    class MainProgram
    {
        // 函数或者方法
        static void Main(string[] args)
        {
            // 一般的结构为:命名空间.类.方法
            CsharpStudyTest.ProgramTest.Test1.Testtest1();
            CsharpStudyTest.ProgramTest.Test2.Testtest2();
        }
    }
}

需要注意的是,只有在对应源文件中的类和方法为 public 公开时别的文件才可以进行访问。
和C++类似,如果使用 using 语句提前声明了命名空间,调用方法时可以省略掉前缀。

using 指令用于引入命名空间,从而可以直接使用该命名空间中的类。

所以上述 MainProgram.cs 也可以写为:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using CsharpStudyTest.ProgramTest;  // 引入命名空间

class Program {
    static void Main(string[] args) {
        Test1 obj1 = new Test1();
        obj1.Testtest1();
        Test2 obj2 = new Test2();
        obj2.Testtest2();
    }
}

C# 还支持 部分类(Partial Class),允许将一个类的定义分散在多个文件中,例如有这么一个项目结构:

MyNamespace/
├── MainProgram.cs
├── MyClassPart1.cs
└── MyClassPart2.cs

那么代码如下:

// 在 MyClassPart1.cs 文件中:
namespace MyNamespace {
    public partial class MyClass {
        public void Method1() {
            System.Console.WriteLine("Method1");
        }
    }
}

// 在 MyClassPart2.cs 文件中:
namespace MyNamespace {
    public partial class MyClass {
        public void Method2() {
            System.Console.WriteLine("Method2");
        }
    }
}

MainProgram.cs 文件中进行调用:

using MyNamespace;

class Program {
    static void Main(string[] args) {
        MyClass obj = new MyClass();
        obj.Method1();
        obj.Method2();
    }
}

C# 与 C++ 头文件的对比

特性 C++ 头文件 C# 代码组织方式
代码声明 在头文件中声明类、函数、变量等 .cs 文件中直接定义类、方法等
代码实现 在源文件 .cpp 中实现头文件中声明的内容 .cs 文件中直接实现类和方法
命名空间 使用 namespace 组织代码 使用 namespace 组织代码
访问其他文件 通过 #include 引入头文件 通过 using 指令引入命名空间
部分类 不支持 支持 partial class

为什么 C# 不需要头文件?

C# 的设计目标是简化开发流程,因此省略了头文件的概念。以下是主要原因:

  1. 单一文件包含声明和实现
  • C# 的类定义和实现都在同一个 .cs 文件中,不需要像 C++ 那样分开声明和实现。
  1. 元数据(Metadata)
  • C# 编译器会生成包含类型信息的元数据,其他代码可以通过元数据访问类和方法,而不需要头文件。
  1. 项目引用
  • 通过项目引用和程序集(Assembly),C# 可以自动解析依赖关系,而不需要手动引入头文件。

20250126 补充:

静态方法应该通过类名直接调用,而不是通过实例对象调用。

静态方法(static method)的特点:

  • 属于类:静态方法属于类本身,而不是类的实例。
  • 无需实例化:可以直接通过类名调用静态方法,而不需要创建类的对象。
  • 不能访问非静态成员:静态方法中不能直接访问类的非静态成员(如实例变量或实例方法),因为非静态成员依赖于类的实例。

错误示例:

public class MyClass
{
    // 静态方法
    public static void StaticMethod()
    {
        Console.WriteLine("This is a static method.");
    }

    // 非静态方法
    public void NonStaticMethod()
    {
        Console.WriteLine("This is a non-static method.");
    }

    public void Test()
    {
        // 错误:在非静态方法中直接调用静态方法
        StaticMethod(); // 这里会报错
    }
}

正确示例,通过类名调用静态方法:

public class MyClass
{
    public static void StaticMethod()
    {
        Console.WriteLine("This is a static method.");
    }

    public void Test()
    {
        // 正确:通过类名调用静态方法
        MyClass.StaticMethod();
    }
}

并且静态方法(static method)中不能直接调用非静态方法(实例方法) 。例如:

namespace CsharpStudyTest
{
    class Program
    {
        // Main 函数必须是静态的
        static void Main(string[] args)
        {
            // 静态方法中不能调用非静态方法,除非通过创建类的实例实例来调用
            TestCode.Test1 obj1 = new TestCode.Test1();
            obj1.TestFunction1();
            // 在非静态方法或静态方法中调用静态方法应该通过类名直接调用,而不是通过实例对象调用
            // TestCode.Test1.TestFunction1();
        }
    }
}
namespace CsharpStudyTest.TestCode
{
    public class Test1
    {
		// 非静态函数,在 Main 函数中调用必须要先实例化
        public void TestFunction1()
        {
            Console.WriteLine("Size of int: {0}", sizeof(int));
            // Console.ReadLine();
        }
    }
}