C# 如何定义基于.NET framework版本的代码?

C# 如何定义基于.NET framework版本的代码?,c#,.net,.net-4.5,C#,.net,.net 4.5,我有一个函数,喜欢利用.NET 4.5中提供的“正确”方式做某事: public DbDataAdapater CreateDataAdapter(DbConnection connection) { #IFDEF (NET45) return DbProviderFactories.GetFactory(connection).CreateDataAdapter(); #ELSE //We can't construct an adapter directl

我有一个函数,喜欢利用.NET 4.5中提供的“正确”方式做某事:

public DbDataAdapater CreateDataAdapter(DbConnection connection)
{
   #IFDEF (NET45)
      return DbProviderFactories.GetFactory(connection).CreateDataAdapter();
   #ELSE
      //We can't construct an adapter directly
      //So let's run around the block 3 times, before potentially crashing
      DbDataAdapter adapter; 

      if (connection is System.Data.SqlClient.SqlConnection)
         return new System.Data.SqlClient.SqlDataAdapter();
      if (connection is System.Data.OleDb.OleDbConnection)
         return new System.Data.OleDb.OleDbDataAdapter();
      if (connection is System.Data.Odbc.OdbcConnection)
         return new System.Data.Odbc.OdbcDataAdapter();
      //Add more DbConnection kinds as they become invented
      if (connection is SqlCeConnection)
         return new SqlCeDataAdapter();
      if (connection is MySqlConnection)
         return new MySqlDataAdapter();
      if (connection is DB2Connection)
         return new DB2DataAdapter();

      throw new Exception("[CreateDataAdapter] Unknown DbConnection type: " + connection.GetType().FullName);
   #END
}
我能找到的唯一方法是为使用此共享代码的所有人提供此功能

这是不会发生的;它必须正常工作,否则就根本不用了

当解决方案针对.NET framework的早期版本时,是否有办法定义非功能性代码

换言之,如果这是一个:

public DbDataAdapter CreateDataAdapter(DbConnection conn)
{
   if (System.Runtime.Version >= 45)
      return DbProviderFactories.GetFactor(connection).CreateDataAdapter();
   else
   {
      //...snip the hack...
   }
}

但是如果目标框架太低,它就不会编译。

只要代码没有JIT,它就可以引用不存在的方法/类/程序集。由于JIT的最小单位是整个函数,因此您需要将代码放入函数中,以引用可能丢失的方法/类,并动态决定何时调用该函数:

public DbDataAdapter CreateDataAdapter(DbConnection conn)
{
   if (System.Runtime.Version >= 45)
   {
      return Hide45DependencyFromJit(connection);
   }
   else
   {
      //...snip the hack...
   }
}

private void Hide45DependencyFromJit(... connection)
{
      return DbProviderFactories.GetFactor(connection).CreateDataAdapter();
}
注:

  • 我不确定4/4.5框架是否存在问题,这种方法在其他“功能缺失”的情况下对我有效
  • 如果不确定,它可能不起作用
  • 您必须将目标框架设置得更高,并且非常小心,避免错误地依赖于新方法

如果优先考虑使用最小编译时设置,那么我只需将检查移到运行时,并使用反射来检查该方法是否可用,如果不可用,则使用变通方法。这是一个附加的好处,即在安装了4.5的客户端上运行的针对.NET 4.0的应用程序将使用更好的方法

样本:

static Func<DbConnection, DbProviderFactory> GetFactoryDelegate;

private static void Main() {
    Console.WriteLine(GetFactory(new SqlConnection()).CreateDataAdapter());
}
private static DbProviderFactory GetFactory(DbConnection connection) {
    if (GetFactoryDelegate == null) {
        var frameworkGetFactoryMethod = typeof (DbProviderFactories).GetMethod(
            "GetFactory", BindingFlags.Static | BindingFlags.Public,
            null, new[] { typeof (DbConnection) }, null);

        if (frameworkGetFactoryMethod != null) {
            GetFactoryDelegate = (Func<DbConnection, DbProviderFactory>)
                Delegate.CreateDelegate(
                    typeof(Func<DbConnection, DbProviderFactory>),
                    frameworkGetFactoryMethod);
        }
        else { GetFactoryDelegate = GetFactoryThroughWorkaround; }
    }
    return GetFactoryDelegate(connection);
}
private static DbProviderFactory GetFactoryThroughWorkaround(
    DbConnection connection) {

    if (connection is SqlConnection)
        return SqlClientFactory.Instance;
    // ... Remaining cases
    throw new NotSupportedException();
}
静态函数GetFactoryDelegate;
私有静态void Main(){
WriteLine(GetFactory(新SqlConnection()).CreateDataAdapter());
}
专用静态DbProviderFactory GetFactory(DbConnection连接){
如果(GetFactoryDelegate==null){
var frameworkGetFactoryMethod=typeof(dbProviderFactorys).GetMethod(
“GetFactory”,BindingFlags.Static | BindingFlags.Public,
null,新[]{typeof(DbConnection)},null);
if(frameworkGetFactoryMethod!=null){
GetFactoryDelegate=(Func)
Delegate.CreateDelegate(
类型(Func),
frameworkGetFactoryMethod);
}
else{GetFactoryDelegate=GetFactoryThroughWorkaround;}
}
返回GetFactoryDelegate(连接);
}
私有静态DbProviderFactory GetFactoryThroughWorkaround(
数据库连接(连接){
if(连接是SqlConnection)
返回SqlClientFactory.Instance;
//…其余案件
抛出新的NotSupportedException();
}

这种方法非常类似于JavaScript世界中当前的最佳实践,即检查功能是否可用,而不是执行浏览器嗅探。由于需要使用反射,与.NET相对应的.NET没有相同的优雅性。但是,如果
动态
的要求是可接受的,则代码可以变得更漂亮。

此答案与标记的答案相同,但对于寻求不基于用户原始post场景的解决方案的其他人来说,更容易理解


使用反射来确定类是否存在。如果是,则动态创建并使用它,否则使用可以为该场景定义的变通类或代码

下面是我用于
aggregateeexception
的代码,它仅为.Net 4及更高版本:

var aggregatException = Type.GetType("System.AggregateException");

if (aggregatException != null) // .Net 4 or greater
{
    throw ((Exception)Activator.CreateInstance(aggregatException, ps.Streams.Error.Select(err => err.Exception)));
}

// Else all other non .Net 4 or less versions
throw ps.Streams.Error.FirstOrDefault()?.Exception 
      ?? new Exception("Powershell Exception Encountered."); // Sanity check operation, should not hit.

这是如何与内联一起工作的?如果.Net 4.0尝试内联
Hide45DependencyFromJit()
方法会发生什么?Hide45DependencyFromJit函数仍然无法编译。@svick-如果它尝试内联,可以在它上面抛出…@IanBoyd-我想我误解了为4.0编译是必需的。。。我想在这种情况下,你是运气不好有单一版本的程序集。。。我会尽量避免使用宏,或者创建两个具有版本相关代码的小程序集,或者使用João Angelo关于在运行时通过反射创建该函数的建议(如果新函数确实比您当前的4.0方法好得多的话)。文档中说,
CreateDataAdapter
从.NET 2.0开始就受到支持了?在.NET 4中是新的。@TimS:我的意思是你可以自己使用连接来获得工厂。@jgauffin我如何使用连接本身来获得工厂?