2009-06-12 6 views
3

У меня возникли проблемы с литьем объекта в общий IList. У меня есть группа заявлений, чтобы попытаться обойти это, но должен быть лучший способ сделать это.C# Generics и Casting Issue

Это мой текущий метод:

string values; 

if (colFilter.Value is IList<int>) 
{ 
    values = BuildClause((IList<int>)colFilter.Value, prefix); 
} 
else if (colFilter.Value is IList<string>) 
{ 
    values = BuildClause((IList<string>)colFilter.Value, prefix); 
} 
else if (colFilter.Value is IList<DateTime>) 
{ 
    values = BuildClause((IList<DateTime>)colFilter.Value, prefix); 
} 
else if (...) //etc. 

То, что я хочу сделать это:

values = BuildClause((IList<colFilter.ColumnType>)colFilter.Value, prefix); 

или

values = BuildClause((IList<typeof(colFilter.ColumnType)>)colFilter.Value, prefix); 

или

values = BuildClause((IList<colFilter.ColumnType.GetType()>)colFilter.Value, prefix); 

Каждый из них производит эту ошибку компилятора: Тип или пространство имен имя «colFilter» не может быть найден (? Вы пропали без вести с помощью директивы или ссылка на сборку)

В моем примере, colFilter.ColumnType является INT, строка, дата и т. д. Я не уверен, почему это не работает.

Любые идеи?

EDIT: Это C# 2.0

РЕДАКТИРОВАТЬ # 2

Вот метод BuildClause (у меня есть перегрузки для каждого типа):

private static string BuildClause(IList<int> inClause, string strPrefix) 
{ 
    return BuildClause(inClause, strPrefix, false); 
} 

private static string BuildClause(IList<String> inClause, string strPrefix) 
{ 
    return BuildClause(inClause, strPrefix, true); 
} 

private static string BuildClause(IList<DateTime> inClause, string strPrefix) 
{ 
    return BuildClause(inClause, strPrefix, true); 
} 
//.. etc for all types 

private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes) 
    { 
     StringBuilder sb = new StringBuilder(); 

     //Check to make sure inclause has objects 
     if (inClause.Count > 0) 
     { 
      sb.Append(strPrefix); 
      sb.Append(" IN("); 

      for (int i = 0; i < inClause.Count; i++) 
      { 
       if (addSingleQuotes) 
       { 
        sb.AppendFormat("'{0}'", inClause[i].ToString().Replace("'", "''")); 
       } 
       else 
       { 
        sb.Append(inClause[i].ToString()); 
       } 

       if (i != inClause.Count - 1) 
       { 
        sb.Append(","); 
       } 
      } 

      sb.Append(") "); 
     } 
     else 
     { 
      throw new Exception("Item count for In() Clause must be greater than 0."); 
     } 

     return sb.ToString(); 
    } 

ответ

5

Невозможно связать перегрузку методов и дженериков: хотя они выглядят одинаково, они очень разные. В частности, перегрузка позволяет делать разные вещи в зависимости от типа используемых аргументов; в то время как дженерики позволяют вам делать то же самое независимо от используемого типа.

Если ваш метод BuildClause перегружен, и каждая перегрузка делает что-то другое (не просто различное по используемому типу, но и по-настоящему другая логика, в данном случае - выбор необходимости добавлять кавычки), то где-то, вам нужно сказать что-то вроде «если тип это делает это, если тип - это то» (я называю это «switch-on-type»).

Другой подход заключается в том, чтобы избежать этой логики «включения-типа» и заменить ее полиморфизмом. Предположим, что у вас есть StringColFilter : ColFilter<string> и IntColFilter : ColFilter<int>, тогда каждый из них может переопределить виртуальный метод от ColFilter<T> и предоставить собственную реализацию BuildClause (или просто часть данных, которая поможет процессу BuildClause). Но тогда вам нужно будет явно создать правильный подтип ColFilter, который просто перемещает логику «на тип» в другое место вашего приложения. Если вам повезет, он переместит эту логику в место вашего приложения, где у вас будет знание того типа, с которым вы имеете дело, и затем вы можете явно создавать разные ColFilters в разных местах вашего приложения и обрабатывать их в общем случае позже.

Рассмотрим что-то вроде этого:

abstract class ColFilter<T> 
{ 
    abstract bool AddSingleQuotes { get; } 
    List<T> Values { get; } 
} 

class IntColFilter<T> 
{ 
    override bool AddSingleQuotes { get { return false; } }  
} 

class StringColFilter<T> 
{ 
    override bool AddSingleQuotes { get { return true; } } 
} 

class SomeOtherClass 
{ 
    public static string BuildClause<T>(string prefix, ColFilter<T> filter) 
    { 
     return BuildClause(prefix, filter.Values, filter.AddSingleQuotes); 
    } 

    public static string BuildClause<T>(string prefix, IList<T> values, bool addSingleQuotes) 
    { 
     // use your existing implementation, since here we don't care about types anymore -- 
     // all we do is call ToString() on them. 
     // in fact, we don't need this method to be generic at all! 
    } 
} 

Конечно, это также получает вас к проблеме, следует ли ColFilter знать о кавычках или нет, но это проблема разработки и заслуживает еще один вопрос :)

Я также поддерживаю другие плакаты, говоря, что если вы пытаетесь создать что-то, что создает SQL-инструкции, объединив строки вместе, вы, вероятно, должны перестать это делать и перейти к параметризованным запросам, которые проще и, что более важно, безопаснее.

2

Что делает функция BuildClause() выглядит так.

Мне кажется, что вы можете создавать BuildClause() как способ на IList, и вы можете добавлять значения вместе. Я предполагаю, что вы просто хотите вызвать метод .ToString() для разных типов.

+0

Я запускаю ваш код. Я сделал values ​​= BuildClause (colFilter.Value, prefix); это кажется ОК. Если вы уверены, что colFilter.Value - это тип IList , все должно быть в порядке. –

+0

, который, похоже, не работает для меня, я получаю ошибку компилятора: что у вас есть перегруженность BuildClause? – Jon

0

Тип для IList должен быть известен во время компиляции. В зависимости от того, что вы хотите сделать, вы можете привести список в IList или IEnumerable (без генериков), а затем перебрать их по объектам

2

Если вы правильно используете дженерики в C# 3.0, вы можете добиться того, что вам нужно через неявное типирование (int C# 2.0 вам может потребоваться указать тип). Если ваш метод BuildClause сделан универсальным, он должен автоматически принимать любой тип передается в его родового параметра (ов):

public IList<T> BuildClause<T>(IList<T> value, object prefix) 
{ 
    Type type = typeof(T); 
    if (type == typeof(string)) 
    { 
     // handle string 
    } 
    else if (type == typeof(int)) 
    { 
     // handle int 
    } 
    // ... 
} 

public class ColumnFilter<T>: 
    where T: struct 
{ 
    public IList<T> Value { get; set; } 
} 

var colFilter = new ColumnFilter<string> 
{ 
    Value = new { "string 1", "string 2", "string 3" } 
} 

IList<string> values = BuildClause(colFilter.Value, prefix); 

С дженериков, вы можете уронить ColumnType свойство вашего ColumnFilter. Поскольку он является общим, наряду с вашим методом BuildClause, вы легко можете определить тип, выполнив typeof (T).

2

I am having trouble casting an object to a generic

Кастинг - это временная операция.

Общая информация о времени компиляции.

Не пересекайте потоки.

Кроме того - если вы использовали достойный генератор параметров sql - он добавит вам одиночные кавычки.

1

Я не понимаю вопроса. Меня устраивает. Это может быть так же просто, как отбросить бросок? Что мне не хватает?

using System; 
using System.Collections.Generic; 
using System.Text; 

namespace ConsoleApplication1 { 

    class Foo<T> : List<T> { 
    } 

    class Program { 
    static void Main(string[] args) { 

    var a = new Foo<int>(); 
    a.Add(1); 
    var b = new Foo<string>(); 
    b.Add("foo"); 
    Console.WriteLine(BuildClause(a, "foo", true)); 
    Console.WriteLine(BuildClause(b, "foo", true)); 

    } 

    private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes) { 
     StringBuilder sb = new StringBuilder(); 

     //Check to make sure inclause has objects 
     if (inClause.Count == 0) 
     throw new Exception("Item count for In() Clause must be greater than 0."); 
     sb.Append(strPrefix).Append(" IN("); 
     foreach (var Clause in inClause) { 
     if (addSingleQuotes) 
      sb.AppendFormat("'{0}'", Clause.ToString().Replace("'", "''")); 
     else 
      sb.Append(Clause.ToString()); 
     sb.Append(','); 
     } 
     sb.Length--; 
     sb.Append(") "); 
     return sb.ToString(); 
    } 

    } 

}