【问题标题】:C# - Does casting preserve the reference to the original object?C# - 强制转换是否保留对原始对象的引用?
【发布时间】:2017-01-20 10:12:37
【问题描述】:

如果我在 C# 中转换一个对象,它是否仍然引用原始对象转换?

例如,如果我创建一个方法来更改对象,然后根据调用的特定实现强制转换该对象,是否会保留原始对象?

    public bool CallChangeString()
    {
        String str = "hello";
        ChangeObject(str);
        return String.Equals("HELLO", str);
    }

    public void ChangeObject(Object obj)
    {
        String str = obj as String;
        str.ToUpper(); // pretend for the sake of this that this changes str to upper case
    }

在这种情况下,String.Equals 会返回 true 还是 false?

是否存在强制转换会导致新对象不保留其引用的情况?

【问题讨论】:

  • 测试时返回什么?上面的代码有什么问题吗?
  • @HenkHolterman Equals 返回一个布尔值。
  • @HenkHolterman,那么当我说“String.Equals 会返回 true 还是 false”时,你认为我的意思是什么?
  • 为什么不直接测试一下?
  • @Shub-Nullgurath 不,它的目的是教人们如何避免问题或如何处理问题,而不是检查是否有

标签: c#


【解决方案1】:

取决于你正在做的演员。您可以考虑两种主要的演员阵容:

  1. 保留身份的人
  2. 那些没有

嗯,这不是很有帮助,这正是你要问的。

什么演员保留身份?所有需要对对象本身进行表示性更改的那些,即构成对象的更改

例子?值类型之间的所有转换:intlongintdouble 等,任何装箱或拆箱转换等。组成 long 的位与组成的位非常不同int

更多示例?任何用户定义的强制转换运算符,显式或隐式。为什么?因为编译器不允许用户定义的保留身份的强制转换只是因为编译器已经为你做了它们:

class Foo : IFoo
{
     public static implicit operator object(Foo foo) => return foo; //Compile time error     
     public static implicit operator IFoo(Foo foo) => return foo; //compile time error.

     public static explicit operator Bar(Foo foo) => return new Bar(foo);
}

注意用户定义转换的一般模式:

  • 任何有效的操作符都会有一个new潜伏 无论它返回什么。那就是马上告诉你 cast 无法保留身份...您正在返回一个新对象。
  • 您无法实现用户定义的身份保留转换。这不是问题,因为没有需要,这些转换已经由 C# 的类型系统提供。

那么,什么是身份保持转换或转换?好吧,那些全部接触对象的; 参考转化

嗯?但是等等,重点是我在 casting 对象,这怎么可能?

在引用转换中,您只是“投射”指向对象的引用。对象总是相同的。当然,这仅在引用类型中有意义,这就是为什么值类型没有保持身份转换的原因;除非被装箱,否则没有对值类型的引用。

例子? objectstringFooIFooGiraffeAnimal

请注意,引用类型也可以很好地实现用户定义的强制转换,但这些不会保留身份。

说了这么多,这是经验法则:

  • 语言类型系统本身提供/允许的任何转换都会保留标识。这些完全是引用转换,仅适用于引用类型。
  • 任何用户定义的强制转换,无论是显式的还是隐式的,保留身份(记住,(long)2 是用户定义的强制转换,必须有人在 System.Int64 类中实现它)。李>

所以,回答你的问题,var str = obj as string 是一个引用转换,这意味着ReferenceEquals(str, obj)truestrobj 指向完全相同的对象,唯一的区别是引用的类型。

【讨论】:

  • 好吧,“保留身份”归结为“(用户定义的)演员表返回什么”?通常它是给定类型的新实例,因此它肯定不能保留身份。
  • @HimBromBeere 没错,问题是:你能想到一个有效的用户定义的不返回新对象的强制转换吗? (我并不是说new 必须是明确的)
【解决方案2】:
  1. 字符串是不可变的,所以你不能改变它们。但是您可以创建新实例:

    myString = myString.Replace("", ".");
    

    请注意,myString 是一个全新的实例,与原始字符串无关。因此,即使您为 myString 分配了不同的内容,这也不会影响您依赖原始实例的调用代码。

    在您的示例中,str.ToUpper() 不会更改任何内容,因为 ToUpper 返回一个新字符串,而不是更改当前 (str) 实例。

  2. casting 为同一个实例创建一个新的reference。因此,对实例的修改会反映在它的所有引用中,无论是强制转换的实例还是只是重新引用

    var a = (MyType) myInstance;
    a.MyProperty = 4;
    

    在最后一条语句之后,amyInstance 都将属性 MyProperty 设置为 4,因为变量 amyInstance 都引用了 MyType 的同一个实例。

【讨论】:

  • 啊,我没有意识到 str.ToUpper() 不会改变字符串本身。感谢那!我已经对其进行了一些小改动。
【解决方案3】:

像这样改变你的方法,看看结果)

public bool CallChangeString()
    {
        String str = "hello";
        ChangeObject(str);
        return String.Equals("HELLO", str);
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 2016-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多