可以声明一个包含扩展的接口吗?
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 关键字不属于那里。
因此,想请教以下问题:
- 是否可以在内部声明一个带有扩展的接口?
- 如果是,我应该改变什么?
- 如果没有,是否有不同的方法来完成我想要的?
非常感谢任何帮助,
丹尼尔
- 不,你不能。那是因为您只能在接口上声明实例方法,而扩展方法必须是静态的。
您可以尝试这样的操作:
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
}
}
在这里,您可以一次决定要做什么,并设置属性。这目前仅适用于属性,不适用于字段,因此您可能需要稍微重构一下以同时使用 FieldInfo
和 PropertyInfo
测试它产生:
mx1 0.786868741170908
mx2 0.434705327001729
mx3 51
refType Object
作为测试库的一部分,我想定义一个接口 '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 关键字不属于那里。
因此,想请教以下问题:
- 是否可以在内部声明一个带有扩展的接口?
- 如果是,我应该改变什么?
- 如果没有,是否有不同的方法来完成我想要的?
非常感谢任何帮助, 丹尼尔
- 不,你不能。那是因为您只能在接口上声明实例方法,而扩展方法必须是静态的。
您可以尝试这样的操作:
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
}
}
在这里,您可以一次决定要做什么,并设置属性。这目前仅适用于属性,不适用于字段,因此您可能需要稍微重构一下以同时使用 FieldInfo
和 PropertyInfo
测试它产生:
mx1 0.786868741170908
mx2 0.434705327001729
mx3 51
refType Object