如果我将流作为传递值类型参数方法传递到方法中,是否需要将流(C#)重置回起始位置

如果我将流作为传递值类型参数方法传递到方法中,是否需要将流(C#)重置回起始位置,c#,C#,我在互联网上看到过几个例子,上面说我必须通过 stream.Seek(0, SeekOrigin.Begin); 但是,如果我将流作为 public static bool ValidateStreamLine(Stream stream) 我还需要重置流吗 请理解,在通常情况下,如果我将int、string、float或任何其他通用变量类型作为 public static bool ValidateInt(int i) int i的值不会改变 在方法对流的反应上,按值传递方法的性质会有什

我在互联网上看到过几个例子,上面说我必须通过

stream.Seek(0, SeekOrigin.Begin);
但是,如果我将流作为

public static bool ValidateStreamLine(Stream stream)
我还需要重置流吗

请理解,在通常情况下,如果我将int、string、float或任何其他通用变量类型作为

public static bool ValidateInt(int i)
int i的值不会改变


在方法对流的反应上,按值传递方法的性质会有什么不同吗?

您似乎误解了参数在C#中的传递方式。默认情况下,所有参数都按C#中的值传递。为了通过引用传递它们,您需要使用一个特殊的关键字:
ref
out
当参数用作给定方法的第二个输出时,通常使用后者。(例如,请参见
int.TryPase

这里需要理解的重要一点是,如果参数的类型是引用类型或值类型,则通过值传递的参数的行为非常不同。这就是你似乎感到困惑的地方

要了解其工作原理,请确保您清楚以下几点:

  • 变量保存值
  • 类型为值类型的变量的值是值类型的实例本身:

  • 类型为引用类型的变量的值不是所述类型的实例。该值是该实例所在的内存地址

  • 那么,现在我们已经清楚了这一点,当您通过值传递参数(C#的默认值)时会发生什么?所发生的事情是创建变量的副本并将副本传递给方法

    如果类型是引用类型,那么真正复制并传递给方法的是存储在变量中的值。那是什么?变量引用的对象所在的内存地址。你看到发生了什么?原始变量和复制的变量都指向同一对象:

    如果类型是值类型,则值类型的值本身将被复制并传递给方法,因此方法无法修改存储在原始变量中的值类型

    public void Increment(int i) { i = i + 1; }
    
    var myInt = 1;
    Increment(myInt);
    Console.WriteLine(myInt); //Outputs 1, not 2. Why? i holds its own copy of 1, it knows nothing about the copy of 1 stored in myInt.
    
    当您通过引用传递参数时,情况会发生变化。现在,传递给方法的参数不是副本,而是原始变量本身。一个合乎逻辑的问题随之而来;这实际上改变了引用类型的行为吗?答案是肯定的,很多:

    public void ByValueCall(string s)
    {
        s = "Goodbye";
    }
    
    public void ByReferenceCall(ref string s)
    {
        s = "Goodbye";
    }
    
    var myString = "Hello!";
    Console.WriteLine(ByValueCall(myString )); //outputs "Hello!"
    Console.WriteLine(ByValueCall(myString )); //outputs "Goodbye!"
    
    这种行为也与值类型相同。这里发生了什么

    当您通过值传递参数时,该方法将获得变量的副本;因此,给参数赋值实际上就是给副本赋值;callsite的原始变量不关心更改其副本的值,无论是值类型还是引用类型。它将保持它一直拥有的价值

    通过引用传递参数时,不是传递副本,而是传递变量本身。在这种情况下,为变量分配一个新值将在调用站点保持。以下交换方法是一个典型示例:

    public void Swap(ref int a, ref int b)
    {
        var temp = a;
        a = b;
        b = temp;
    }
    
    var i1 = 1;
    var i2 = 2;
    Swap(ref i1, ref i2);
    var b = i1 == 2; //true
    b = i2 == 1; //true
    
    因此,在完成所有这些之后,您应该了解以下行为的原因:

    public ResetStream(Stream stream)
    {
       stream.Seek(0, SeekOrigin.Begin); 
    }
    
    var myStream = new ...
    myStream.Read(bytes, 0, 1024);
    ResetStream(myStream);
    var isAtOrigin = myStream.Position == 0; //Returns true!
    

    对流是访问方法,而不是内容。无论如何,你不能通过值传递它,它是一个引用type@PanagiotisKanavos这是否意味着我可以在ValidateStreamLine方法中重置,它也可以工作?@PanagiotisKanavos我很挑剔,但它是按值传递的,它是按值传递的引用类型这个问题没有通用的答案。除了某些流无法重置之外,您还需要阅读任何接受
    参数的方法的文档,以确定它的要求和期望是什么。@PanagiotisKanavos您的意思是,不能按值传递它?当然可以,这是在C#中传递任何参数的默认方式。类型是引用类型或值类型与如何在C#中传递参数完全无关。@BenKnoble Lol,我完全忘记了这一点。修正了,谢谢。谢谢你的解释:)还有一个问题。除了stream、string类型之外,还有哪些行为类似的类型?有完整的列表吗?@SimonLoh绝大多数都是引用类型。规则很简单:结构是值类型,类是引用类型。如果不确定,只需检查类型的定义即可。
    public void ByValueCall(string s)
    {
        s = "Goodbye";
    }
    
    public void ByReferenceCall(ref string s)
    {
        s = "Goodbye";
    }
    
    var myString = "Hello!";
    Console.WriteLine(ByValueCall(myString )); //outputs "Hello!"
    Console.WriteLine(ByValueCall(myString )); //outputs "Goodbye!"
    
    public void Swap(ref int a, ref int b)
    {
        var temp = a;
        a = b;
        b = temp;
    }
    
    var i1 = 1;
    var i2 = 2;
    Swap(ref i1, ref i2);
    var b = i1 == 2; //true
    b = i2 == 1; //true
    
    public ResetStream(Stream stream)
    {
       stream.Seek(0, SeekOrigin.Begin); 
    }
    
    var myStream = new ...
    myStream.Read(bytes, 0, 1024);
    ResetStream(myStream);
    var isAtOrigin = myStream.Position == 0; //Returns true!