C# - 对象创建

8

我是一名PHP程序员已经有一段时间了。两天前,我去参加了一次工作面试,他们要求我使用ASP.NET(C#)完成一个任务。我真的很想摆脱PHP的影响并学习一门可以挑战我的好语言。所以,我有一个问题:

所有实例都必须在运行时实例化吗?在PHP中,我可以这样做...

class SomeObject {}

$objString = "SomeObject";
$objInstance = new $objString();

我无法在C#中这样做,可能是因为它是一种编译型语言。在C#中,我必须创建一个工厂模式来实例化对象。这也意味着,如果我要在该工厂中实例化10个对象,则会有10个if语句,这很丑陋。
我发现Activator对象及其createInstance()方法,但我无法让它正常工作。还有反射,但这两者(据我所知)都会影响性能。
那么,是否有一种动态创建对象的方法,或者在C#中,我可以立即创建程序将使用的所有对象,这真的很诱人吗?
编辑:
好的,假设我有5个对象用于5个不同的场合。我运行程序,程序评估需要其中之一,并实例化它。其他四个从未被实例化。我关闭程序。
第二次,我使用不同的参数运行程序,并创建了其中的2个对象,而其他三个从未存在过。
在PHP中很容易实现。让我们把Activator和其他工具放在一边,那么在C#世界中,当我知道只可能使用其中一个对象时,创建所有5个对象是否是一个好习惯?

3
我不懂 PHP,但在 C# 中你可以同时创建对象并初始化。你不需要使用工厂或 Activator 对象 - 只需使用 SomeObject someObj = new SomeObject(); 即可。 - Tim
10
在.NET中动态构建对象是可行的,但你需要学习整个语言、运行时、平台,而不仅仅是语法。我的意思是,如果你尝试像在PHP中一样使用C#进行编程,你会有非常糟糕的体验。 - Lasse V. Karlsen
我不是很懂 PHP,但你应该仔细考虑你尝试如何使用它...因为毕竟,你正在创建它以便在某处使用它的值?听起来你可能想要使用泛型或接口,或者你真的是在问一个 XY 问题,其中一个好的答案将取决于具体情况。 - Sayse
请查看我的更新答案,希望这能有所帮助。 - nozzleman
实际上,我有3个共享相同接口的对象,只有在满足特定条件时才需要实例化其中一个。事实上,我有3个对象并不会造成任何影响。我也可以有1000个对象。 - Mario Legenda
显示剩余5条评论
4个回答

6

我不知道是否理解您的问题有误,但在C#中创建给定类SomeObject的对象非常简单,只需要按照以下步骤操作:

var obj = new SomeObject();

使用Activator类,它应该像下面这样之一:

var obj2 = Activator.CreateInstance(typeof(SomeObject)); 
var obj3 = Activator.CreateInstance<SomeObject>();`

var someObjType = Type.GetType("SomeObject");  // if you have to use a string
var obj4 = Activator.CreateInstance(someObjType);

请注意,大多数情况下不需要使用Activator类来实例化对象。如果您在编译时知道类的Type,则第一个示例是标准方式。 更新 关于您的更新,由于我不知道细节,我想到的是懒加载。由于在C#中,包括应用程序入口在内的所有内容都是对象,您可以像以下示例中使用带有备份字段的属性来解决问题。
class Program
{
    // backing fields
    private SomeObject obj1;
    private SomeObject obj2;
    private SomeObject obj3;
    private SomeObject obj4;
    private SomeObject obj5;

    // this way, obj1 only gets instanciated when needed
    public SomeObject Obj1 
    {
        get
        {
            if (obj1 == null)
            { 
                 obj1 = new SomeObject(); 
            }
            return obj1;
        }
    }

    // same for the other objects

    [...]

}

如果你担心对象的内存使用情况,我建议你学习如何正确实现IDisposable。
更新2:为了提供由@Mad Sorcerer在评论中推荐的可能性,您可以使用Lazy类支持字段,这会减少你的工作量,效果与以前的更新相同。
class Program
{
    // Lazy backing fields
    private Lazy<SomeObject> obj1 = new Lazy<SomeObject>();
    private Lazy<SomeObject> obj2 = new Lazy<SomeObject>();
    private Lazy<SomeObject> obj3 = new Lazy<SomeObject>();
    private Lazy<SomeObject> obj4 = new Lazy<SomeObject>();
    private Lazy<SomeObject> obj5 = new Lazy<SomeObject>();

    // this way, obj1 only gets instanciated when needed
    public SomeObject Obj1 
    {
        get { return obj1.Value; }
    }

    // same for the other objects

    [...]

}

是的,谢谢。这个问题的所有答案基本上都告诉我应该停止用 PHP 的思维方式,开始像 C# 一样思考。我的意思是,我已经学习了一天半的 C#,经验随时间而来,对吧? - Mario Legenda
我写了一篇文章大小的问题,其实只需要问“在C#中是否有一种懒惰实例化对象的方法?”就可以了。这是我的疏忽!非常感谢。 - Mario Legenda
@MadSorcerer 谢谢你的建议,我已经在答案中加入了它。 - nozzleman
@MarioLegenda 大多数 PHP 开发人员在开发方面有不好的习惯(无意冒犯任何人)。在 PHP 中,可以以类似于 C# 的方式创建类和调用方法,但很少见到这些做法在 PHP 社区中使用。你想要声明性对象而不是动态对象的原因是为了让代码在以后更清晰明了。通常阅读一年后的代码时,“这是从哪里来的”思考通常并不理想。这并不是说 PHP 不好,但有一种名为 FaceBook 的 HACK 编程语言和不使用动态对象的原因。 - vipero07

5

是的,你可以在C#中动态创建对象。但不如在PHP中容易。

正如你发现的那样,有Activator。如果使用正确,它可以很好地工作。 :) 还有直接反射(Activator 也基于反射)。但是,如果朴素地使用它们,它们会变得很慢。

使用Expression类型,可以缓存实例化对象的逻辑。第一次仍然很慢,但如果你希望重复创建相同类型的对象,这种方法效果很好(如果不需要,那么通过Activator慢的事实就无关紧要了:))。

话虽如此,在大多数情况下,你不应该像你的示例中那样需要动态创建对象。应该利用C#中强大的编译时类型检查,而不是回避它。编写代码来实例化已知类型的对象最高效,也最安全。

对于那些相对较少需要动态行为的时候,也是可能的。


2

所以PHP代码...你是通过字符串选择要实例化的类吗?

你需要先查找类型,然后使用Activator进行实例化。

Type classType = Type.GetType("SomeObject");
object instance = Activator.CreateInstance(classType);

你可以查找它的构造函数,并使用反射调用该构造函数,代码如下:
Type classType = Type.GetType("SomeObject");
var ctorInfo = classType.GetConstructor(Type.EmptyTypes);
object instance = ctorInfo.Invoke(new object[] {});

你可以将构造函数缓存为委托,这可以消除由于查找资源而带来的性能损失#

请注意,Type.GetType存在一些问题。如果SomeObjectSystem命名空间中,则上述代码将起作用。您可能需要添加命名空间、类名,有时还需要添加程序集信息。总体而言,最安全的方法是使用AssemblyQualifiedName


对于初学者来说,如果SomeObject类被限定在某个命名空间中,比如ConsoleApplication1,则应该明确显示:GetType("ConsoleApplication1.SomeObject"); - Diligent Key Presser

1

在C#中创建类的实例有几种方法。

通常的方法是使用new

MyType myInstance = new MyType();

对于动态创建对象,例如基于其名称,Activator.Createinstance()可以工作,但您必须知道定义该类的程序集,以便正确创建该实例。

然而,特别是如果您刚开始学习,我不会打扰您进入这个领域。
C#是一种静态类型语言,这意味着类型的解析在编译期间完成。

此外,请考虑IDE Visual Studio将极大地帮助您,因为它可以扫描项目中的文件并了解您在代码中键入的每个成员的类型。

静态类型语言有助于避免纯动态语言中出现的确切问题:它们迫使您保持一致,代价是要求您在声明时明确指定类型(尽管使用var关键字,C#可以在避免通常需要的冗长性方面提供很多帮助)。

因此,整个重点是确保您在应用程序中声明所有所需的类型。

如果你需要让一个特定的类成员接受来自多个其他类的数据,你可以使用继承,创建一个基类,其他类将从中继承一些行为,或者使用接口,描述多种类型的公共成员。每种方法都有其优点和缺点
如果你正在尝试C#,只是进行实验,我建议使用LINQpad。它可能会在学习过程中帮助你进行实验。
我还建议在深入尝试运行时操作之前,先了解惯用的C#,并远离任何非规范化的东西。

你可以探索的一个领域是 动态 类型。它不像真正的动态语言那样灵活,但它是处理类型不确定性的一种方式。
然而,它有性能缺陷,如果你想从Visual Studio的Intellisense中受益,并且想从IDE和编译器提供的正常类型检查中受益,就应该避免使用它。


我在上面提过这个问题,因此我将把它复制/粘贴给您。假设我有5个对象池中的1个对象,这些对象将在字符串的变化值上创建。这意味着我需要5个if语句(或case或其他语句)来创建所需的对象。这在C#中是可取和正常的做法吗? - Mario Legenda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接