【问题标题】:C# How to have an object available in another method?C#如何让对象在另一种方法中可用?
【发布时间】:2014-02-20 04:11:39
【问题描述】:

我有两种方法,在 ConnecttoDB 方法中我创建了一个名为 connection 的对象,但我需要在第二种方法中使用该对象来指定 SQLiteCommand。我怎样才能知道该对象在 InsertField 方法中可用?

public class Database
{
    private static string _datasource;
    private static string _table;
    private static string _field;
    private static string _values;

    public static void ConnecttoDB(string datasource)
    {
        _datasource = datasource;
        SQLiteConnection connection = new SQLiteConnection();
        connection.ConnectionString = "Data Source: " + _datasource;
        connection.Open();
    }
    public static void InsertField(string table, string field, string values)
    {
        SQLiteCommand command = new SQLiteCommand(/*connection*/);

        _table = table;
        _field = field;
        _values = values;
        String.Format("INSERT INTO {0} {1} VALUES{2}", _table, _field, _values);
    }
}

【问题讨论】:

  • 你必须以某种方式暴露对象。要么使其成为类的属性/字段,要么将其传递给方法。我建议使用某种 ORM 而不是滚动您自己的数据访问。
  • 为什么你有一个字符串表示的静态字段?您可以改为将其设为 SQLiteConnection 字段。我个人不会一开始就使用这种设计,但这是一种不同的方法。 (我不会有字段/表/值的静态变量,也不会使用字符串格式将值嵌入 SQL。改用参数化 SQL!)
  • 在这种特殊情况下,您实际上不应该拥有两种方法。创建数据库连接对象应该在使用它的方法中进行,并且应该在 using 块内完成,以便在操作完成时将其释放。
  • @PhilVallone:当然——虽然我可能一开始不会在 Web 环境中使用 SQLite :)
  • @user2684089:对。无论如何,我仍然会使用“根据需要打开和关闭”模型。

标签: c# object methods


【解决方案1】:

你的班级设计很奇怪。为什么无缘无故将_table_field_values 声明为类字段,但不要为connection 这样做,这样可以解决您的问题?您可以直接使用tablefieldvalues 参数,而无需事先将它们分配给字段。

让我建议另一种方法:

public class Database
{
    private string _connectionString;

    // Pass the datasource through the constructor.
    public Database(string datasource)
    {
        _connectionString = "Data Source: " + datasource;
    }

    public void InsertField(string table, string field, object value)
    {
        using (var conn = new SQLiteConnection(_connectionString)) {
            string sql = String.Format("INSERT INTO {0} ({1}) VALUES (@1)",
                                       table, field);
            var command = new SQLiteCommand(sql, conn);
            command.Parameters.AddWithValue("@1", value);
            conn.Open();
            command.ExecuteNonQuery();
        }
    }
}

(未测试,SQLite 的调用约定可能不同)

我正在为value 使用object 类型,它与任何类型都兼容。我也将此值作为命令参数传递给命令。例如,DateTime 值会自动转换为 SQLite 的正确格式。它还可以防止SQL injection。 (我希望表名和字段名是应用程序定义的,而不是用户输入的。)

由于连接池,系统会保持连接打开一段时间并自动重用打开的连接。因此,connection.Open(); 调用并不会在每次调用时真正物理地重新打开连接。另一方面,这种方法可以防止连接打开很长时间并使用系统资源而不被使用。

using 语句会自动关闭连接并释放连接使用的资源 - 即使在代码块到达其末尾之前应该发生异常。

你可以这样使用它:

var db = new Database("my data source");
db.InsertField("myTable", "myField", 100);

更新

如果您需要可变数量的参数,我会为字段和值使用数组:

public void InsertField(string table, string[] fields, object[] values)
{
    if (fields.Length != values.Length) {
        throw new ArgumentException(
            "The fields and values arrays must have the same length");
    }

    string fieldNames = String.Join(", ", fields); // ==> "name1, name2, name3"

    // ==> "@p0, @p1, @p2"
    string paramNames = String.Join(", ",
        Enumerable.Range(0, fields.Length)
            .Select(i => "@p" + i)
            .ToArray()
    );
    using (var conn = new SQLiteConnection(_connectionString)) {
        string sql = String.Format("INSERT INTO {0} ({1}) VALUES ({2})",
                                   table, fieldNames, paramNames);
        var command = new SQLiteCommand(sql, conn);
        for (int i = 0; i < values.Length; i++) {
            command.Parameters.AddWithValue("@p" + i, values[i]);
        }
        conn.Open();
        command.ExecuteNonQuery();
    }
}

用法:

var db = new Database("my data source");
db.InsertField("myTable", 
               new string[]{ "Name", "Quantity", "Price", "Date" }, 
               new object[]{ "Apples", 12, 2.65m, DateTime.Now });

【讨论】:

  • 我对编程很陌生,有时我不懂简单的概念。我不知道我可以将对象作为类字段。谢谢!
  • 我确实有一个问题,希望你能帮助我。只要我只想在给定时间添加 1 个值,它就可以正常工作,但是如果我尝试插入多个值,我会收到错误消息“2 列的 1 个值”。我用这条线运行它 db.InsertField("Item", "Name, Category","Apple,Grocery");如何分隔值?
  • 也试过 db.InsertField("Item", "Name, Category","'Apple','Grocery'");
  • 我现在真的很困惑......我试过 db.InsertField("Item", "Name, Category","'Apple','Grocery'");它不会工作,但如果我将对象更改为字符串,它就可以工作......
  • 我添加了一个示例,说明如何解决可变数量字段的问题。
【解决方案2】:

解决您当前面临的问题:

在这种情况下,您实际上不应该这样做。你的两种方法应该是一种方法:

public static void InsertField(string table, string field, string values)
{
    using (SQLiteConnection connection = new SQLiteConnection())
    {
        using (SQLiteCommand command = new SQLiteCommand(connection))
        {
            _table = table;
            _field = field;
            _values = values;
            String.Format("INSERT INTO {0} {1} VALUES{2}", _table, _field, _values);
            // and the rest of the code, incomplete in the example
        }
    }
}

构建 SQL 连接对象并不是一个昂贵的过程。而且它只是一行代码。因此,通过将其卸载到另一种方法,您实际上并没有节省太多。然而,您正在所做的是引入了许多其他潜在的运行时问题(与您目前容易看到的编译时问题相反),例如资源泄漏和处置对象错误。

尽可能将连接和命令对象封装到最接近其使用的地方,并在使用完它们后尽快处理掉它们。底层系统经过了很好的优化,可以在您在另一个方法(或再次调用相同的方法)中再次需要它们时重新创建它们。

更具体地解决您的问题:

考虑从一种方法传递到另一种方法的变量的范围、方法的范围等。您可以选择。

变量是方法所在对象的属性吗?例如,在Person 对象中,您可能有一个称为Height 的东西。因为这描述了人,所以它可以是类级别的:

public int Height { get; private set; }

public void OneMethod(int someValue)
{
    // more code
    Height = someValue;
    // more code
}

public void AnotherMethod()
{
    // more code
    someOtherObject.DoSomethingWithHeight(Height);
    // more code
}

如果该值实际上并未描述对象,而是在某个较小的范围内,请考虑将其传递给方法。可能是这样的:

public void SomeMethod()
{
    // more code
    var someValue = 123;
    // more code
    AHelperMethod(someValue);
    // more code
}

private void AHelperMethod(int aValue)
{
    // more code
    someOtherObject.DoSomethingWithValue(aValue);
    // more code
}

在第一种情况下,两种方法都使用描述该对象的属性对对象本身执行一些操作。在第二种情况下,第二种方法只是对象内部的东西,并且仅用于支持第一种方法的更高级别的操作。它可能只是第一个方法中的内联代码,但为了重用、可读性、维护抽象级别或任何数量的重构原因,它已被提取到自己的方法中。

【讨论】:

    【解决方案3】:

    最简单的方法是将 SQLiteConnection 对象声明为类级变量。然后您的 InsertField 方法将使用类级别变量,如下所示:

    SQLiteCommand command = new SQLiteCommand(connection);

    【讨论】:

    • @David 同意!我个人不会走他走的路。这就是为什么我提到“最简单的”......不是最好的:)
    【解决方案4】:

    将连接作为私有实例字段,然后可以在所有方法中使用。同时删除静态关键字。静态很少需要,而且通常很糟糕。

    public class Database
    {
        private string _datasource;
        private string _table;
        private string _field;
        private string _values;
        private SQLiteConnection _connection;
    
        public void ConnecttoDB(string datasource)
        {
            _datasource = datasource;
            _connection = new SQLiteConnection();
            _connection.ConnectionString = "Data Source: " + _datasource;
            _connection.Open();
        }
        public void InsertField(string table, string field, string values)
        {
            SQLiteCommand command = new SQLiteCommand(_connection);
    
            _table = table;
            _field = field;
            _values = values;
            String.Format("INSERT INTO {0} {1} VALUES{2}", _table, _field, _values);
        }
    }
    

    【讨论】:

    • 很明显这个类是不完整的。我并没有解决代码的所有问题 - 或完成它 - 我正在回答如何使该信息可用于另一种方法的具体问题。
    • 那么...通过创建更困难的问题来解决问题?
    【解决方案5】:

    要么保持全局,要么将其作为参数传递给 InsertFields。在后一种情况下,您必须在某处创建它,然后传递给您的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-11
      • 2017-10-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多