对Indexers的一个限制是索引器没有定义存储位置,因此索引器生成的值不能作为ref或out参数传递给方法。 我想知道我们为索引器定义的数组不是存储位置?
答案 0 :(得分:3)
我将打破你问题的每一部分并试着帮助你。
对Indexers的一个限制是索引器没有定义存储位置
如果说"存储中的某个位置不能保证在该索引的类实现中被抽象地定义"然后是的,这是正确的,抽象地说你在索引器的索引处定义了一个值,但这并不能保证你正在访问一个逻辑位置(在抽象级别,在低级别,所有东西都有一个位置)。基本上索引是一种很好的方式来表示一个方法,它接受一个值并返回一个值,一个变量指示位置和使用括号的语法,以及等号来确定调用哪个方法(获取或设置)。我觉得我要离开主题,但你可以在 on MSDN.上查找关于索引实现的更多信息但是就像方法你必须使它有意义。这是一个失败的例子,在实现的后端没有实际位置。
public class MyClass
{
private void Set(int i,string value)
{
Console.WriteLine("Your Index:{0}\r\nSet Value:{1}",i,value);
}
public string this[int i]
{
get
{
if(i<0)
return "less than zero";
if(i==0)
return "This is zero";
else if(i==1)
return "This is one";
else if(i==2)
return "this is two";
else
return "more than two";
}
set
{
//value is a key word in a setter
//representing the value on you are attempting to set
Set(i,value);
}
}
}
为什么你会这样做我不确定,但是如果你确实想要的话,索引器只是表达一种方法的一种很好的方式,它可以作为一个索引,例如一个字典或一个列表,虽然有人可能会试图在技术上争论这个例子中的getter它仍然没有意义,也不应该使用索引来表达方法
因此,索引器生成的值不能作为ref或out参数传递给方法。
由于您通过索引访问的数据被封装在类中,除非该类公开了对该数据的引用而您无法访问它,因此您无法在方法调用中将其作为ref或out参数传递给它indexer属性,所以我们需要查看访问索引器是否在内存中公开了一个位置
不,关键词`ref`和`out`基本上告诉IL让方法占用一个内存地址,`out`要求内存中的位置被分配一个新值,`ref`不需要改变但是仍然允许它发生,因为.NET中的所有语言都不支持所有索引和属性,它们是通过更改&#34; get&#34;中的指令来实现的。和&#34;设置&#34;在方法调用中,`ref`和`out`需要在内存中传递变量的位置,减少到IL试图处理索引器的get / set,因为out变量等同于尝试处理方法或新变量作为语法无效的`ref`或`out`参数
你不能,原因是因为你在使用索引器时调用了一个方法,假设你有这个作为你想要调用的方法
public void CreateNew(out object target)
{
target = new object();
}
当你在某个级别的指令上调用CreateNew方法时:
在两种情况下调用索引器
索引器&#34;获取&#34;方法出现在对象被索引并尝试访问的位置。发生这种情况时,会对某些方法进行方法调用,该方法表示具有类似
的签名的get方法
ValueType _get_index_IndexType_ValueType( IndexType index)
因此,如果编译器将您对此的调用解析为out参数,那么就像尝试将引用传递给尚未在内存中分配了位置的变量。这就是为什么它不能与&#34; Get&#34;方法,这是通过设计完成的,逻辑上你无法从内存中某个对象的内存中访问一个变量。
索引器&#34; Set&#34;当对象被索引并且在等号的左侧时,会出现方法,在内部,它会替换为表示具有此类签名的set方法的某种方法
void _set_index_IndexType_ValueType(IndexType index, ValueType Value)
因此,如果调用减少到这个,那就像尝试访问方法调用的内存中的位置一样,这不是我们想要的,我们想做的是在给出一个新的时调用set方法变量到索引,并在我们尝试访问它时得到。但是根据设计,这是不允许的,因为你可以自己轻松地做到这一点......
如果这仍然没有意义,请尝试考虑下面的类,而不是使用索引器方法,我们只使用带索引的Get和Set
public class MyFooIndexableObject
{
/* Note that "ValueType" and "IndexType" are
* just place holders for whatever type you
* decide to make as your return type and
* index type respectively
*
* Using a regular dictionary and an
* extra variable to implement a default
* dictionary so it is not like the example
* is doing nothing.
*/
private Dictionary _internalCollection;
private readonly ValueType _defaultValue = new ValueType();
public void FooSet(IndexType index, ValueType value)
{
if( index == null)
//want to disallow index being null
throw new NullArgumentException("index");
if(_internalCollection==null)
_internalCollection = new Dictionary();
if ( value == null || value == _defaultValue )
// want to remove it
{
_internalCollection.Remove(index);
}
else
_internalCollection[index]=value;
}
/* The Examples FooSet and FooGet
* would be similar method constructs to
* the ones made behind the scenes when
* you define the getter and setter for
* your indexed object
*/
public ValueType FooGet(IndexType index)
{
if( _internalCollection == null
|| !_internalCollection.Contains(index) )
return new _defaultValue;
return _internalCollection[index];
}
public bool TryGetValueAtFirstNonDefault(out IndexType outIndex,
out ValueType outValue)
{
outParam = outIndex = null;
if(_internalCollection!=null)
{
// no need to check we maintain this in the setter and getter
var temp= _internalCollection.FirstOrDefault();
if(temp!=null)
{
outParam = temp.Value;
outIndex = temp.Key;
}
}
return outParam != null;
}
private static void Swap( ref ValueType someRefParam,
ref ValueType otherRefParam)
{
var temp = someRefParam;
someRefParam = otherRefParam;
otherRefParam = temp;
}
//use this instead
public void SwapValueAtIndexes(IndexType index1, IndexType index2)
{
var temp = this.FooGet(index1);
this.FooSet(index1, this.FooGet(index2) );
this.FooSet(index2, temp);
}
public static void Main(string[] args)
{
var indexable = new MyFooIndexableObject();
var index1 = new IndexType(0);
var index2 = new IndexType(1);
ValueType someValue;
//do someValue = indexable[index1]
someValue = indexable.FooGet(index1);
//do indexable[index1] = new ValueType()
indexable.FooSet(index1,new ValueType());
//this does not make sense will not work
//do Swap( out indexable[index1], out indexable[index2] )
//just look how you would try to do this
Swap( ref indexable.FooGet(index1), ref indexable.FooGet(index2));
//Swap is looking for reference to a location in memory
//but the method is returning the value of an object reference
//which you can store in a variable with a location in memory
//but has yet been assigned to one
//Please note the whole idea of "location in memory" is abstract
//it does not technically mean an actual location in physical
//memory but probably an abstraction handled by .NET,
//don't try to hard to make sure you have the technical part
//100% correct, you are significantly detached from the metal
//when coding at this level...the basic idea is the same
//as physical memory locations on a machine
//However, you can accomplish the same things that you would
//want to accomplish with "out" and "ref" by creating methods
//that take the indexed object and an index, such as the
//SwapValueAtIndex method
indexable.SwapValueAtIndex(index1,index2);
//While precisely what SwapValueAtIndex does may
//not translate to what Swap does logically
//it is the same thing, which is good enough for us
}
}
即使您无法获得对象的实际引用,您也可以将索引和索引对象传递给方法,这将有效地提供与变量引用相同的效果,因为您可以访问它使用索引及其所在的对象
public void Swap(MyIndexedObject o, string indexer, object newValue,
ref object oldValue)
{
if(o.Contains(indexer))
{
oldValue = o[indexer];
}
else
oldValue = null;
o[indexer]=newValue;
}
public bool TryGetValue(MyIndexedObject o, string index, out object value)
{
value=null;
if(o.Contains(index))
{
value = o[value];
return true;
}
return false;
}
public void TrySwapValue(MyIndexedObject o, string indexer1, string indexer2)
{
object valHolder1=null,valHolder2=null;
if(TryGetValue(o,indexer1, out valHolder1))
{
Swap(o, indexer2, valHolder1,ref valHolder2);
o[indexer1] = valHolder2;
}
}
正如您所看到的,如果您有对象,那么逻辑上可以将索引用作位置(在索引对象实现有意义的情况下),即使用索引对象的地方
如果您仍然需要对索引对象的引用,您可以定义一个具有索引并获取并设置对象值的类,在此您可以包含诸如历史记录之类的内容
public class MyObject : Dictionary{}
public class MyPlaceHolder
{
public MyPlaceHolder(string index, MyObject target)
{
Index = index;
TargetObject = target;
}
public string Index {get; private set;}
public MyObject TargetObject {get; private set;}
public object Value
{
get
{
return TargetObject[Index];
}
set
{
var prev = TargetObject[Index];
TargetObject[Index] = value;
_prevVals.Push(prev);
}
}
private Stack _prevVals = new Stack();
public bool UndoSet()
{
if(!_preVals.Count() == 0)
{
Value._prevVals.Pop();
return true;
}
return false;
}
}
我想知道我们为索引器定义的数组是不是存储位置?
是的,数组是一个位置,但索引定义不是该地址的直接反映。对象的索引是对索引概念的抽象,它允许您根据传入其中的索引值访问对象,它不一定会这样做,但从技术上讲它应该是一种与位置无关但不应该的方法。
然而,对象不暴露下面的实际位置的方式是正确的,你使用封装来隐藏索引方法指定的位置的方式,这是我们进行面向对象编程的原因之一我不喜欢如果0是实现级别的位置,只要在我使用它时有意义
我感觉不好只创建一个实际上非常糟糕的Indexed对象的例子,并且希望没有人错误地认为这是一个好主意,所以将说明为什么隐藏位置是有意义的,这是抽象背后的目的索引
让我们说我想制作一个双键字典,我知道在我的代码的某些部分我将实现它,但我不知道如果你有多个人在工作因此,您不希望人们在编写类时等待,因此您可以定义界面,并在其他程序员工作时实现它
public interface IMyDoubleStringDictionaryBase<T>
{
T this[string index1, string value2]
{
get;set;
}
}
你决定使用嵌套字典来制作它,这就是你想出来的
public class MyDoubleStringDictionary<T> : IMyDoubleStringDictionaryBase<T>
{
private Dictionary<string,Dictionary<string,T>> _baseCollection;
public T this[string index1, string index2]
{
get
{
if(_baseCollection.ContainsKey(index1))
{
var nextDict = _baseCollection[index1];
if(nextDict.ContainsKey(index2))
{
return nextDict[index2];
}
}
return default(T);
}
set
{
Dictionary<string,T> nextDict;
if(_baseCollection.Contains(index1))
{
nextDict = _baseCollection[index1];
}
else
{
nextDict = new Dictionary<string,T>();
_baseCollection.Add(index1,nextDict);
}
nextDict[index2] = value;
}
}
}
由于某种原因,您在生产环境中无法使用Dictionary类,而这可能对您没有意义,您被告知仅使用Array数据结构创建一个,您需要自己定义的所有其他抽象数据结构。您决定创建一个桶哈希,它使用密钥的两个哈希并混合它们
public class MyNewDoubleStringDictionary<T> : IMyDoubleStringDictionaryBase<T>
{
private class Node<T>
{
public Node<T> Next;
public string Key1,Key2;
public T Value;
}
private const int ARRAY_SIZE = 1024;
private Node<T>[] _internalCollection = new Node<T>[ARRAY_SIZE];
private int GetIndex(string key1, string key2)
{
const int key1mask = 0x0F0F0F0F;
const int key2mask = 0xF0F0F0F0;
var key1 = key1mask & key1.GetHashCode();
var key2 = key2mask & key2.GetHashCode();
var result = ((key1 | key2) & 0x7FFFFFFF)% ARRAY_SIZE;
return result;
}
private Node<T> GetOrMakeNode(string key1,string key2)
{
int index = GetIndex(key1,key2);
Node<T> currNode=_internalCollection[index];
if(currNode == null)
{
_internalCollection[index] = currNode = new Node<T>();
}
else
{
while(!(currNode.Key1.Equals(key1)
&&currNode.Key2.Equals(key2))
if(currNode.Next!=null)
{
currNode = currNode.Next;
}
else
{
currNode.Next = new Node<T>();
currNode = currNode.Next;
}
}
if(currNode.Key1 == null || currNode.Key2 == null)
{
currNode.Key1 = key1;
currNode.Key2 = key2;
}
return currNode;
}
public this[string index1, string index2]
{
get
{
var node = GetOrMakeNode(index1,index2);
return node.Value;
}
set
{
var node = GetOrMakeNode(index1,index2);
node.Value = value;
}
}
}
即使你的要求和实施有所改变,它也不会打断你团队的任何工作,因为你没有引用对象的内部工作,所以它不可能弄乱他们的工作。
你不关心这个位置的位置,如果实际的实现正在查看某个位置,你应该真的担心,只要知道你必须以某种方式连接索引,你就会能够使用它
答案 1 :(得分:2)
http://msdn.microsoft.com/en-us/library/vstudio/6x16t2tx.aspx
Indexer只是特殊的getter和setter。而ref或out总是局部变量。索引器甚至不必指向存储位置,但可以返回计算值。
索引器甚至不必在阵列上使用。例如,在矢量图像中,我可以定义索引器myvectorimage [x] [y],使得它返回x和y位置的颜色,但数据永远不会以这种方式存储。