可以声明一个包含扩展的接口吗?

Possible to declare an interface containing an extension?

作为测试库的一部分,我想定义一个接口 'this object knows how to initialize itself randomly'。如果随机填充对象的成员是引用,则随机初始化应该能够将null分配给这些成员。

如果我为一个 class 这样做,代码可能如下所示

public class QWorker
{
    double mxVal = 0;

    public void fillRandomly(System.Random xRng)
    {
        mxVal = xRng.NextDouble();
    }
}

public class QBoss
{
    public QWorker mxWorker;

    void fillRandomly(System.Random xRng)
    {
        if (xRng.Next() % 2 == 1)
            x1 = null;
        else
        {
            x1 = new QWorker();
            x1.fillRandomly(xRng);
        }
    }    
}

现在,如果 QBoss 有多个引用类型的成员,if/else 必须为每个成员完成。它看起来很难看,维护起来也很麻烦。为了cimrcumvent,我想出了下面的示例代码:

public interface QIRandomizable<T> where T : new()
{
    static void fillRandomly(this System.Random xThis, ref T xRef); // XXX
}

class QWorker : QIRandomizable<QWorker>
{
    public double mxDouble;
}

public static class QWorkerExtensions 
{
    public static void fillRandomly(this System.Random xThis, ref QWorker xRef)
    {
        if ((xThis.Next() % 2) == 1)
            xRef = null;
        else
        {
            xRef = new QWorker();
            xRef.mxDouble = xThis.NextDouble();
        }
    }
}

public class QBoss : QIRandomizable<QBoss>
{
    public QWorker mx1;
    public QWorker mx2;

    public static void fillRandomly(this System.Random xThis, ref QBoss xRef)
    {
        xRef = new QBoss();
        xThis.fillRandomly(ref xRef.mxMember1);    // can be null
        xThis.fillRandomly(ref xRef.mxMember2);    // can be null
    }
}

然而,这无法编译,第一个问题出现在标记为 XXX 的行上 - static 关键字不属于那里。

因此,想请教以下问题:

  1. 是否可以在内部声明一个带有扩展的接口?
  2. 如果是,我应该改变什么?
  3. 如果没有,是否有不同的方法来完成我想要的?

非常感谢任何帮助, 丹尼尔

  1. 不,你不能。那是因为您只能在接口上声明实例方法,而扩展方法必须是静态的。

您可以尝试这样的操作:

public interface IDoesSomething
{
    void fillRandomly(Random r);
}
public class QBoss
{
    public double mx1 { get; set; }
    public double mx2 { get; set; }
    public int mx3 { get; set; }

    public object refType { get; set; }

    public void fillRandomly(Random r)
    {
        FillRandom(GetProps(this), this, r);    
    }
}


public static IEnumerable<PropertyInfo> GetProps(object blah)
{
    return blah.GetType().GetProperties();
}

public static void FillRandom(IEnumerable<PropertyInfo> obj, object onObj, Random r)
{
    Action<PropertyInfo, object> setVal = (prop, val) => { prop.SetValue(onObj, val); };
    foreach (var o in obj)
    {
        if (!o.PropertyType.IsValueType)
        {
            if (r.Next() % 2 != 1)
            {
                var v = Activator.CreateInstance(o.PropertyType);
                setVal(o, v);
                var id = v as IDoesSomething;
                if (id != null)
                    id.fillRandomly(r);
            }
        }
        if (o.PropertyType == typeof(double))
            setVal(o, r.NextDouble());
        if (o.PropertyType == typeof(int))
            setVal(o, (int)(r.NextDouble() * 100));

        //etc, etc
    }
}

在这里,您可以一次决定要做什么,并设置属性。这目前仅适用于属性,不适用于字段,因此您可能需要稍微重构一下以同时使用 FieldInfoPropertyInfo

测试它产生:

mx1 0.786868741170908
mx2 0.434705327001729
mx3 51
refType Object