【问题标题】:Why DataSet is being passed by reference without explicitly passing out or ref parameter? [duplicate]为什么 DataSet 是通过引用传递而没有显式传递或 ref 参数? [复制]
【发布时间】:2016-12-22 22:52:49
【问题描述】:

这段代码我看不懂,这里DataSet对象和字符串变量的实现是一样的,但是输出不同。我可以看到字符串变量输出背后的逻辑,但是对于DataSet,我看不懂明白为什么!

class Program
    {

        static void Main(string[] args)
        {
            DataSet ds = new DataSet();
            FillDS(ds);
            PrintDS(ds);
            string name = "old";
            AssignString(name);
            PrintString(name);
        }
        private static void AssignString(string name)
        {
            name = "new name";
        }
        private static void PrintString(string name)
        {
            Console.WriteLine(name);
        }
        private static void FillDS(DataSet ds)
        {
            ds.Tables.Add(new DataTable("tbl1"));
            ds.Tables.Add(new DataTable("tbl2"));
        }
        private static void PrintDS(DataSet ds)
        {
            foreach (DataTable item in ds.Tables)
            {
                Console.WriteLine(item.TableName);
            }
        }
    }
//Output:
//tbl1
//tbl2
//old

【问题讨论】:

  • “相同的实现”是什么意思?在AssignString 中,您正在为name 参数分配一个新值。在FillDS 中,您没有为ds 参数分配任何内容。您正在调用更改其内容的方法。你能看出这两个动作不一样吗?如果您在FillDS 中分配了ds = new DataSet(),您会发现您作为参数传入的数据集不会发生变化。
  • 我可能不会为传递给 AssignString 的参数分配新值,我可以这样做:name.Remove(0, 1);输出:旧
  • 它们都是引用类型,为什么对于字符串它不从原始副本中获取副本,而是从数据集中获取副本?顺便说一句,如果我用 int 替换字符串,结果仍然相同
  • 阅读documentation for String.Remove。它不会改变字符串,它只是返回一个新字符串。碰巧 String 没有改变它的方法;它们都只是返回不同的新字符串。
  • Main 函数中尝试此代码:var s = "test"; s.Remove(0,1); Console.WriteLine(s);。你会发现String.Remove() 永远不会改变字符串。然后试试这个:var s2 = s.Remove(0,1); Console.WriteLine(s2); 你会发现String.Remove() 返回一个与原始字符串不同的字符串。但它并没有改变原来的。

标签: c#


【解决方案1】:

因为DataSetstring 都是引用类型,并且在引用类型中,它始终是传递的对象的内存地址,它位于托管堆上的位置。

【讨论】:

  • 字符串也是引用类型。
  • 感谢@Habib 的指点,我错过了这一点
【解决方案2】:

字符串是不可变的,而且你没有修改字符串参数(不是你可以),而是为你的参数分配了一个新的引用。原件留在原地。

使用DataSet,您正在修改它的内容,因为它是一个可变引用类型,您可以看到调用者的变化。

Main中尝试以下操作

    DataSet ds = new DataSet();
    ds.Tables.Add(new DataTable("tbl3"));
    FillDS(ds);

然后在FillDS 中为您的DataSet 分配一个新引用,例如:

private static void FillDS(DataSet ds)
{
    ds = new DataSet(); //Here 
    ds.Tables.Add(new DataTable("tbl1"));
    ds.Tables.Add(new DataTable("tbl2"));
}

您会看到您的DataSet 仍然保留旧值,并且在调用FillDS 后没有任何修改

【讨论】:

  • 如何判断一个类型是否可变?我发现int 也是不可变的!
  • 文档...查看 Eric Lippert 的 blog posts
【解决方案3】:

stringDataSet 都是引用类型。因此,在这两种情况下,您都会传递引用。

AssignString 方法中,您不会更改您传递的string 实例,而是分配一个新实例 ("new name") 给变量namename 变量实际上是一个参数这一事实并不重要(尽管重新分配参数被认为是一种不好的做法)。

FillDS 中,您不会重新分配新实例,而是通过ds.Tables.... 访问和操作传递的实例的属性。

【讨论】:

  • 那么string 类型中的属性是否可以与本示例中的DataSet 执行相同的操作?
  • @MohamedAhmed No. string 被设计为不可变string 实例上的每个可能的操作都返回一个具有更改值的 new 实例,但不会更改当前实例。
  • C# 中是否有可变和不可变类型的主题/主题?我想我之前没有接触过这个话题
  • 因为我发现在这个例子中int 类型的行为与string 相同,如何判断一个类型是否可变?
  • @MohamedAhmed 这是一个开始,正如我所说,谷歌是你的朋友...我将从第二个搜索结果开始:blogs.msdn.microsoft.com/ericlippert/2007/11/13/…
猜你喜欢
  • 2015-03-24
  • 2013-05-22
  • 2011-04-05
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 2020-03-26
  • 2012-05-22
  • 1970-01-01
相关资源
最近更新 更多