我有类似的东西:
public interface IThing
{
string Id { get; }
}
public class Ball : IThing
{
public string Id { get; }
}
public class Car : IThing
{
public string Id { get; }
}
对于我的3-4个功能,我想要同等对待Ball
和Car
。我使用界面,所以我不必制作重载方法(一个用于汽车,一个用于球)。
最后,如果它是Ball
或Car
,那么我的函数会有不同的逻辑。我得到一个IEnumerable<IThings>
,我希望根据它包含的内容将其转换为IEnumerable<Car>
或IEnumerable<Ball>
。如果它由混合物组成,我希望它失败。它必须是所有汽车或所有球。
我尝试过类似的事情:
var things = (inputs is IEnumerable<Ball>) ? input.Locations.Cast<Ball>() : input.Locations.Cast<Car>()
但它不喜欢那样。我可以拥有1个变量的推荐方法是什么?
编辑:
我想把它变成一个变量的原因是因为我将它发送到一个重载的方法。所以我想这样做:
var things = (inputs is IEnumerable<Ball>) ? input.Locations.Cast<Ball>() : input.Locations.Cast<Car>()
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
而不是:
if (inputs is IEnumerable<Ball>)
{
var things = input.Locations.Cast<Ball>();
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
}
else
{
var things = input.Locations.Cast<Car>();
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
}
答案 0 :(得分:1)
你可以做一个转换方法,但这仍然会破坏一些原则,因为你仍然需要放置一个if
语句。
我不确定,您正在以正确的方式使用接口以实现您想要实现的目标。
如果您希望car
在特定情况下与ball
的行为不同,那么在汽车中的实施应做与球中的实施不同的东西
不要试图从外面调整界面。实现必须这样做。
为什么不在DoMySpecialStuff
中创建一个方法IThing
,而只是在这个只在所有元素上调用DoMySpecialStuff
的特殊方法迭代你的枚举?
这就是你可以避免if语句的方式。
我刚看到您使用overloadedMethod
所以它可以像这样工作:
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
var results = callOverLoadedFunction(thingsSet);
}
void OverLoadedFunction(IThing thing)
{
thing.DoSpecialStuff(); // This does different things in car/ball
}
答案 1 :(得分:1)
你尝试这个问题:
inputs is IEnumerable<Ball>
因为只包含IEnumerable<IThing>
类型元素的Ball
不是与IEnumerable<Ball>
相同的类型。您真的别无选择,只能枚举您的收藏,以确定每个项目是否与您需要的类型相匹配。您可以使用.Cast<...>()
并处理InvalidCastException
,但这有点像hacky。另一种方法是使用OfType<...>
:
var cars = inputs.OfType<Car>();
var balls = inputs.OfType<Ball>();
现在你可以按照自己的意愿处理它们,例如:
if(balls.Any() && cars.Any())
{
//You're not allowed to have balls and cars together
throw new Exception(...);
}
但是,你真的打破了这里的open/closed principle SOLID,你似乎应该考虑更高层次的目标。
答案 2 :(得分:1)
您可以使用LINQ
将球和车相互分开IEnumerable<Ball> balls = things.OfType<Ball>().Count() == things.Count() ? things.OfType<Ball>() : null; //or whatever you want
如果你想让它失败并像一线解决方案那样试试
TX Begin
Select Balance
Logic in PHP
Exception
Rollback
Commit
答案 3 :(得分:1)
我决定重做我前段时间做过的事情:将部分枚举的IEnumerator<>
重新转换为完整的IEnumerable<>
。这解决了一个我认为很重要的问题:你不应该两次枚举&#34;未知&#34; IEnumerable<>
(对于&#34;未知&#34;我的意思是IEnumerable<>
你没有用相同的方法手工制作,但来源不明),因为无法保证它可以完成,即使可以完成,也可能导致生成IEnumerable<>
两次所需的大量工作。
public class RemainingIEnumerator<T> : IEnumerable<T>
{
public IEnumerable<T> Enumerable { get; set; }
public int Nulls { get; set; }
public T First { get; set; }
public IEnumerator<T> Enumerator { get; set; }
public IEnumerator<T> GetEnumerator()
{
var enumerator = Enumerator;
if (enumerator == null)
{
return Enumerable.GetEnumerator();
}
return GetEnumerableRemaining().GetEnumerator();
}
private IEnumerable<T> GetEnumerableRemaining()
{
var enumerator = Enumerator;
Enumerator = null;
int nulls = Nulls;
Nulls = 0;
T first = First;
First = default(T);
for (int i = 0; i < nulls; i++)
{
yield return default(T);
}
yield return first;
while (enumerator.MoveNext())
{
yield return enumerator.Current;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static bool Is<T>(IEnumerable<T> enu, Type type, out IEnumerable<T> enu2)
{
IEnumerator<T> enumerator = null;
int nulls = 0;
try
{
enumerator = enu.GetEnumerator();
while (enumerator.MoveNext())
{
var current = enumerator.Current;
if (current == null)
{
nulls++;
continue;
}
enu2 = new RemainingIEnumerator<T>
{
Enumerable = enu,
Nulls = nulls,
First = current,
Enumerator = enumerator,
};
enumerator = null;
return current.GetType() == type;
}
// Only nulls case
enu2 = new T[nulls];
return false;
}
finally
{
if (enumerator != null)
{
enumerator.Dispose();
}
}
}
如果第一个非Is<T>()
元素的类型为null
,则type
函数返回true。它返回一个可以使用的新IEnumerable<>
,通过&#34; magic&#34;,重复使用传递给IEnumerable<>
的{{1}}(在某种程度上,它会重新选择可选的初始Is<>
s,第一个找到的元素和未使用的剩余null
)。
使用示例:
IEnumerator<>
答案 4 :(得分:0)
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
List<IThing> testCollection = new List<IThing>();
testCollection.Add(new Ball());
testCollection.Add(new Car());
try
{
if (testCollection[0] is Ball)
{
Console.WriteLine(testCollection.Cast<Ball>().Count().ToString());
}
else
{
Console.WriteLine(testCollection.Cast<Car>().Count().ToString());
}
}
catch(InvalidCastException ex)
{
Console.WriteLine("Mix isn't allowed!");
}
}
}
public interface IThing
{
string Id { get; set;}
}
public class Ball : IThing
{
public string Id { get;set; }
}
public class Car : IThing
{
public string Id { get;set; }
}
此代码将在调用Cast<Ball>
时抛出InvalidCastException,因为Car
对象无法强制转换为Ball
。如果我没弄错的话,这应该做你想要的。
代码只检查第一个元素的类型,因为List
不应该混合,可以假设List
中的所有其他对象应该具有相同的类型,如果不是那样,在我看来,问题是如何编写的,适当的原因就是抛出异常。
答案 5 :(得分:0)
请注意,IEnumerable<>
是协变的,也就是说,只要需要IEnumerable<Derived>
,您就可以替换IEnumerable<Base>
。
如果你只有纯容器的东西,那只是同一类容器在给定的容器中,那么你应该制作那个特定类型的容器(它将在某个时候作为IEnumerable传递)。例如,工厂可以使用List<car>
等代码生成真实的汽车列表IEnumerable<Thing> things = factory.produceList(ThingTypes.Car);
。与所有类型一样,IEnumerable<>
对象保留其实际类型信息,即使它们被分配给更基本类型的引用也是如此。此类型可用于区分运行时IEnumerable<>
的实际类型。
也许某些代码更容易理解。我用两个不同类型的元素创建了两个IEnumerable<I>
,它们都实现了相同的接口I
。正如我所说,只要IEnumerable<T>
实施IEnumerable<I>
,我就可以为T
分配I
。
using System;
using System.Collections.Generic;
namespace ConsoleApplication34
{
interface I { };
class T1 : I { }
class T2 : I { }
class Program
{
// strongly typed arrays get assigned to base type IEnumerables.
static IEnumerable<I> i1 = new T1[] { new T1(), new T1() };
static IEnumerable<I> i2 = new T2[] { new T2(), new T2() };
static void Main(string[] args)
{
// Note: compile-time type of array elements is IEnumerable<I>!
IEnumerable<I>[] iEnumArr = new IEnumerable<I>[] { i1, i2 };
foreach (IEnumerable<I> ie in iEnumArr)
{
// ... but the run-time types of the IEnumerable objects
// are actually different.
Console.WriteLine("ienumerable is of T1: " + (ie is IEnumerable<T1>));
Console.WriteLine("ienumerable is of T2: " + (ie is IEnumerable<T2>));
}
}
}
}
输出
ienumerable is of T1: True
ienumerable is of T2: False
ienumerable is of T1: False
ienumerable is of T2: True
修改编辑:我看到您正在使用thingsSet
这是一个真正的IEnumerable<Thing>
。没错,那么型式测试不再适用。
编辑:我的编辑有点不清楚,但我认为你的重载方法有两个(或n个)版本,一个用于 IEnumerable<car>
,另一个用于IEnumerable<ball>
。在这种情况下,我会首先完成与Thing
的具体类型无关的所有事情,然后仅区分重要部分。例如:
for (var i = 0; i < numRequests; i++)
{
var thingsSet = things.Skip(i * 1000).Take(1000);
// I may see your problem: Now with thingsSet we have true
// Enumerables of Thing, and the tests below are always false.
// Hm.
var carSet = thingsSet as IEnumerable<car>;
var ballSet = thingsSet as IEnumerable<ball>;
bool results;
if(carSet != null ) { results = callOverLoadedFunction(carSet); }
else if(ballSet != null) { results = callOverLoadedFunction(ballSet); }
else { throw /*...*/}
}
这个解决方案有一点代码味道;理想情况下,调用代码不关心具体的事物类型。一种可能性是离开&#34;分支&#34;对于Thing
的不同类型。或者,如果不可能,请在内部提供单个callNonOverLoadedFunction(IEnumerable<Thing>)
,然后向调用者隐藏。这些函数可能更接近Things
实现和&#34;知道&#34;存在哪种不同类型的Things
;从维护角度来看,您的调用代码不会,也不想知道。 击>