【问题标题】:How could I prevent from using some specific classes?我怎样才能防止使用某些特定的类?
【发布时间】:2015-05-14 16:03:52
【问题描述】:

我想自动化一些测试来检查程序员编写高效算法的能力。在此示例中,算法是在 int 数组中执行简单的二进制搜索。

public class ComplexityPractice
{
    public bool BinarySearch(int [] sortedArray, int value)
    {
        // to be implement 
    }
}

注意:此代码是在受限域中通过反射加载的。

实现此方法的最简单方法当然是来自 .NET 库的Array.BinarySearch(sortedArray, value)。但我的目标是检查程序员自己生成代码的能力,所以问题是:

如何防止程序员使用 Array 类的函数?

【问题讨论】:

  • 你不是读代码吗?
  • OP 询问的是如何创建认证式测试来检查学生的代码,而不是如何编写单元测试或执行代码分析。我认为这个问题需要澄清
  • 您需要某种静态分析工具来定义自定义规则,然后弄清楚如何强制它们使用规则。
  • 如果我搜索“C# Binary Search”,结果的第一页有 4 个实现。限制 Array.BinarySearch 不会阻止学生偷懒。
  • 如果这是为了在求职面试中测试程序员而不是为了学生学习算法,我实际上宁愿让所有没有使用 Array.BinarySearch 的人都失败 :) 这更容易检查!

标签: c# restriction


【解决方案1】:

方法一:

检查代码是否有效,然后测试它是否包含不允许但您必须忽略字符串文字的方法调用。
但是,如果Reflection 可用,这不会阻止用户按名称调用方法。

bool validCode = true;
string[] unallowedMethods = new string[] 
{
  "Array.BinarySearch",
  ...
};
string sourceCode = ...;
ICodeCompiler codeCompiler = ...;
CompilerResults results = codeCompiler.CompileAssemblyFromSource(parameters, sourceCode);
validCode = result.Errors.Count == 0;
if (validCode)
 foreach (method in unallowedMethods)
   if (sourceCode.Contains(method)))
   //improve this by checking if occurrence is not a string literal in program 
   {
      validCode = false;
      break;
   }

方法二:

使用 Mono Cecil 测试一个方法是否调用另一个方法:
Take a look at this answer

static class MethodDefinitionExtensions
{
    public static bool CallsMethod(this MethodDefinition caller, 
        MethodDefinition callee)
    {
        return caller.Body.Instructions.Any(x => 
            x.OpCode == OpCodes.Call && x.Operand == callee);
    }
}

class Program
{
    private static AssemblyDefinition _assembly = AssemblyDefinition.ReadAssembly(
        System.Reflection.Assembly.GetExecutingAssembly().Location);

    private static void Method1()
    {
        Method2();
    }

    private static void Method2()
    {
        Method1();
        Method3();
    }

    private static void Method3()
    {
        Method1();
    }

    private static IEnumerable<MethodDefinition> GetMethodsCalled(
        MethodDefinition caller)
    {
        return caller.Body.Instructions
            .Where(x => x.OpCode == OpCodes.Call)
            .Select(x => (MethodDefinition)x.Operand);
    }

    private static MethodDefinition GetMethod(string name)
    {
        TypeDefinition programType = _assembly.MainModule.Types
            .FirstOrDefault(x => x.Name == "Program");
        return programType.Methods.First(x => x.Name == name);
    }

    public static void Main(string[] args)
    {
        MethodDefinition method1 = GetMethod("Method1");
        MethodDefinition method2 = GetMethod("Method2");
        MethodDefinition method3 = GetMethod("Method3");

        Debug.Assert(method1.CallsMethod(method3) == false);
        Debug.Assert(method1.CallsMethod(method2) == true);
        Debug.Assert(method3.CallsMethod(method1) == true);

        Debug.Assert(GetMethodsCalled(method2).SequenceEqual(
            new List<MethodDefinition> { method1, method3 }));
    }
}

【讨论】:

    【解决方案2】:

    我会考虑使用面向方面的编程。

    具体来说,我会结帐PostSharp

    PostSharp 提供方法边界,允许您在输入方法之前取消执行该方法。

    这是一个例子:

    •OnEntry - 在方法体执行之前

    •OnExit - 始终在方法执行完毕时调用,即使出现错误也是如此

    •OnSuccess - 仅在方法执行完毕且没有异常时调用

    •OnException - 仅当方法由于未处理的异常而停止执行时调用

    http://www.postsharp.net/blog/post/Day-4-OnMethodBoundaryAspect

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      • 2011-05-27
      • 2014-06-05
      • 1970-01-01
      相关资源
      最近更新 更多