【问题标题】:Generically typed HashSet泛型 HashSet
【发布时间】:2016-12-07 19:04:27
【问题描述】:

我已经找到了一些关于这个主题的信息,但都是关于典型类的——我不知道如何将下面的类转换为通用版本。

我继承了现有代码,该代码使用 SqlHashSet 类为数据库的参数设置整数值。它看起来像这样:

namespace Services
{
    public class SqlHashSet : HashSet<int>, IEnumerable<SqlDataRecord>
    {
        SqlDataRecord ret = new SqlDataRecord(new SqlMetaData("value", SqlDbType.Int));       
        foreach (var data in this)
        {
            ret.SetValue(0, data);
            yield return ret;
        }
    }
}

由于代码的结构,我必须使用 SqlHashSet。我的问题是我需要一个 strings 的 SqlHashSet。

我在想也许我可以在类声明中使用HashSet&lt;T&gt;,但我还必须检查值是 SqlDbType.int 还是 SqlDbType.VarChar 类型

所以,我的问题是:

  1. 我可以这样做吗?

  2. 我需要做什么才能使用&lt;T&gt;

  3. 也许没有第三个问题;也许我可以在使用泛型类型后逐步执行代码并弄清楚如何确定 SqlDbType 以将值设置为。

【问题讨论】:

  • Can I do this? foreach 循环,yields 结果必须是方法体的一部分,它不能单独存在于类体中。
  • What do I need to do to use &lt;T&gt;?
  • 您可以采取简单的方法,使用两个通用参数,一个用于HashSet,一个用于SqlDbType。你的班级看起来像SqlHashSet&lt;TManaged, TSql&gt;。或者,重写代码以不使用此 SqlHashSet 并坚持使用库类型。
  • 这个类中T是如何使用的?是否只有HashSet&lt;T&gt; 基类需要?
  • @Igor 没错,但它必须作为普通参数传递给构造函数。

标签: c# .net generics hashset


【解决方案1】:

你可以添加一个基类:

public abstract class SqlHashSetBase<T> : HashSet<T>, IEnumerable<SqlDataRecord>
{
    protected abstract SqlDbType DbType { get; }

    IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()
    {
        SqlDataRecord ret = new SqlDataRecord(new SqlMetaData("value", this.DbType));
        foreach (T data in this)
        {
            ret.SetValue(0, data);
            yield return ret;
        }
    }
}

然后更改您现有的课程:

public class SqlHashSet : SqlHashSetBase<int> {
    protected override SqlDbType DbType {
        get { return SqlDbType.Int; }
    }
}

并添加一个新的:

public class SqlStringHashSet : SqlHashSetBase<string> {
    protected override SqlDbType DbType {
        get { return SqlDbType.NVarChar; }
    }
}

您可能需要考虑将T =&gt; SqlDbType 映射提取到它自己的类中并将其作为参数传递,而不是使用抽象方法。您可能还想考虑使用组合,而不是直接从 HashSet&lt;T&gt; 继承。

【讨论】:

  • 一旦我纠结于 SqlDbType 和 DbType 我最终使用了你的答案。我仍然没有让它工作得很好。对 SQL Server 代码完全陌生,这种废话很难!
  • 嘿,李:我必须让这个工作或废弃它并使用 ids。我知道在宇宙历史的某个地方,有人必须尝试将字符串表发送到 sql server db。我觉得这很接近。事实上,现在太近了,不能回头。我复制了您的代码(逐字逐句),但执行卡在 SqlHashSetBase 中的无限循环中。有一点帮助,如果可以的话。
  • @Patricia - 我认为你必须明确实现IEnumerable&lt;SqlDatatRecord&gt; 以阻止它从HashSet&lt;T&gt; 隐藏GetEnumerator()。然后您可以迭代集合中的项目,而不是递归调用GetEnumerator。查看更新。
  • OK - 我得到新的 SqlDataRecord(new SqlMetaData("value", this.DbType)) 语句,我收到错误“dbType NVarChar 对于这个构造函数无效。
  • @Patricia - 看起来您必须使用 SqlMetaData 的不同构造函数来传递最大长度的 string。您所有的字符串列都使用相同的最大长度吗?否则,如果您事先知道列,则可以从列名 => 长度添加查找。
【解决方案2】:

(请注意,您发布的代码无法编译;您不能直接在类中编写语句,它们必须在方法中)

您无法创建真正的泛型类,因为您需要根据T 的类型使用不同的SqlDbType。但如果你不介意做一些丑陋的事情,你可以这样做:

public class SqlHashSet<T> : HashSet<T>, IEnumerable<SqlDataRecord>
{
    private static readonly SqlDbType _sqlDbType = GetSqlDbType();
    private static SqlDbType GetSqlDbType()
    {
        if (typeof(T) == typeof(int))
            return SqlDbType.Int;
        if (typeof(T) == typeof(string))
            return SqlDbType.String;

        ...

        throw new InvalidOperationException($"Can't find the SqlDbType for {typeof(T)}");
    }

    ...

    SqlDataRecord ret = new SqlDataRecord(new SqlMetaData("value", _sqlDbType));
}

如果您不介意必须为每种值类型创建单独的类的缺点,Lee 的回答中建议的方法类似但更简洁。

【讨论】:

  • 嘿,一旦我纠结于 SqlDbType 和 DbType ,我最终使用了 Lee 的答案。我仍然没有完全正确地工作,但我将不得不给他积分。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
  • 2011-09-26
  • 2018-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多