ZoeAppClass/Library/PackageCache/com.unity.addressables@1.1.10/Runtime/AddressablesImpl.cs

759 lines
30 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEngine.AddressableAssets.Initialization;
using UnityEngine.AddressableAssets.ResourceLocators;
using UnityEngine.ResourceManagement;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.ResourceManagement.Util;
using UnityEngine.SceneManagement;
#if UNITY_EDITOR
[assembly: InternalsVisibleTo("Unity.Addressables.Editor.Tests")]
#endif
[assembly: InternalsVisibleTo("Unity.Addressables.Tests")]
namespace UnityEngine.AddressableAssets
{
internal class AddressablesImpl
{
ResourceManager m_ResourceManager;
IInstanceProvider m_InstanceProvider;
public IInstanceProvider InstanceProvider
{
get
{
return m_InstanceProvider;
}
set
{
m_InstanceProvider = value;
var rec = m_InstanceProvider as IUpdateReceiver;
if (rec != null)
m_ResourceManager.AddUpdateReceiver(rec);
}
}
public ISceneProvider SceneProvider;
public ResourceManager ResourceManager
{
get
{
if (m_ResourceManager == null)
m_ResourceManager = new ResourceManager(new DefaultAllocationStrategy());
return m_ResourceManager;
}
}
List<IResourceLocator> m_ResourceLocators = new List<IResourceLocator>();
AsyncOperationHandle<IResourceLocator> m_InitializationOperation;
Action<AsyncOperationHandle> m_OnHandleCompleteAction;
Action<AsyncOperationHandle<SceneInstance>> m_OnSceneHandleCompleteAction;
Action<AsyncOperationHandle> m_OnHandleDestroyedAction;
Dictionary<object, AsyncOperationHandle> m_resultToHandle = new Dictionary<object, AsyncOperationHandle>();
HashSet<AsyncOperationHandle<SceneInstance>> m_SceneInstances = new HashSet<AsyncOperationHandle<SceneInstance>>();
internal int SceneOperationCount { get { return m_SceneInstances.Count; } }
internal int TrackedHandleCount { get { return m_resultToHandle.Count; } }
public AddressablesImpl(IAllocationStrategy alloc)
{
m_ResourceManager = new ResourceManager(alloc);
SceneManager.sceneUnloaded += OnSceneUnloaded;
}
private void OnSceneUnloaded(Scene scene)
{
foreach (var s in m_SceneInstances)
{
if (!s.IsValid())
{
m_SceneInstances.Remove(s);
break;
}
if (s.Result.Scene == scene)
{
m_SceneInstances.Remove(s);
m_resultToHandle.Remove(s.Result);
m_ResourceManager.ReleaseScene(SceneProvider, s);
break;
}
}
m_ResourceManager.CleanupSceneInstances(scene);
}
public string StreamingAssetsSubFolder
{
get
{
return "aa";
}
}
public string BuildPath
{
get { return "Library/com.unity.addressables/StreamingAssetsCopy/" + StreamingAssetsSubFolder + "/" + PlatformMappingService.GetPlatform(); }
}
public string PlayerBuildDataPath
{
get
{
return Application.streamingAssetsPath + "/" + StreamingAssetsSubFolder + "/" +
PlatformMappingService.GetPlatform();
}
}
public string RuntimePath
{
get
{
#if UNITY_EDITOR
return BuildPath;
#else
return PlayerBuildDataPath;
#endif
}
}
public void Log(string msg)
{
Debug.Log(msg);
}
public void LogFormat(string format, params object[] args)
{
Debug.LogFormat(format, args);
}
public void LogWarning(string msg)
{
Debug.LogWarning(msg);
}
public void LogWarningFormat(string format, params object[] args)
{
Debug.LogWarningFormat(format, args);
}
public void LogError(string msg)
{
Debug.LogError(msg);
}
public void LogException(AsyncOperationHandle op, Exception ex)
{
Debug.LogErrorFormat("{0} encountered in operation {1}: {2}", ex.GetType().Name, op.DebugName, ex.Message);
}
public void LogErrorFormat(string format, params object[] args)
{
Debug.LogErrorFormat(format, args);
}
public string ResolveInternalId(string id)
{
var path = AddressablesRuntimeProperties.EvaluateString(id);
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_XBOXONE
if (path.Length >= 260 && path.StartsWith(Application.dataPath))
path = path.Substring(Application.dataPath.Length + 1);
#endif
return path;
}
public IList<IResourceLocator> ResourceLocators
{
get
{
return m_ResourceLocators;
}
}
internal bool GetResourceLocations(object key, Type type, out IList<IResourceLocation> locations)
{
key = EvaluateKey(key);
locations = null;
HashSet<IResourceLocation> current = null;
foreach (var l in m_ResourceLocators)
{
IList<IResourceLocation> locs;
if (l.Locate(key, type, out locs))
{
if (locations == null)
{
//simple, common case, no allocations
locations = locs;
}
else
{
//less common, need to merge...
if (current == null)
{
current = new HashSet<IResourceLocation>();
foreach (var loc in locations)
current.Add(loc);
}
current.UnionWith(locs);
}
}
}
if (current == null)
return locations != null;
locations = new List<IResourceLocation>(current);
return true;
}
internal bool GetResourceLocations(IEnumerable<object> keys, Type type, Addressables.MergeMode merge, out IList<IResourceLocation> locations)
{
locations = null;
HashSet<IResourceLocation> current = null;
foreach (var key in keys)
{
IList<IResourceLocation> locs;
if (GetResourceLocations(key, type, out locs))
{
if (locations == null)
{
locations = locs;
if (merge == Addressables.MergeMode.UseFirst)
return true;
}
else
{
if (current == null)
{
current = new HashSet<IResourceLocation>();
foreach (var loc in locations)
current.Add(loc);
}
if (merge == Addressables.MergeMode.Intersection)
current.IntersectWith(locs);
else if (merge == Addressables.MergeMode.Union)
current.UnionWith(locs);
}
}
else
{
//if entries for a key are not found, the intersection is empty
if (merge == Addressables.MergeMode.Intersection)
{
locations = null;
return false;
}
}
}
if (current == null)
return locations != null;
if (current.Count == 0)
{
locations = null;
return false;
}
locations = new List<IResourceLocation>(current);
return true;
}
public AsyncOperationHandle<IResourceLocator> InitializeAsync(string runtimeDataPath, string providerSuffix = null)
{
//these need to be referenced in order to prevent stripping on IL2CPP platforms.
if (string.IsNullOrEmpty(Application.streamingAssetsPath))
Debug.LogWarning("Application.streamingAssetsPath has been stripped!");
#if !UNITY_SWITCH
if (string.IsNullOrEmpty(Application.persistentDataPath))
Debug.LogWarning("Application.persistentDataPath has been stripped!");
#endif
if (string.IsNullOrEmpty(runtimeDataPath))
return ResourceManager.CreateCompletedOperation<IResourceLocator>(null, string.Format("Invalid Key: {0}", runtimeDataPath));
m_OnHandleCompleteAction = OnHandleCompleted;
m_OnSceneHandleCompleteAction = OnSceneHandleCompleted;
m_OnHandleDestroyedAction = OnHandleDestroyed;
m_InitializationOperation = Initialization.InitializationOperation.CreateInitializationOperation(this, runtimeDataPath, providerSuffix);
m_InitializationOperation.Completed += (x) => ResourceManager.ExceptionHandler = LogException;
return m_InitializationOperation;
}
public AsyncOperationHandle<IResourceLocator> InitializeAsync()
{
if (!m_InitializationOperation.IsValid())
return InitializeAsync(ResolveInternalId(PlayerPrefs.GetString(Addressables.kAddressablesRuntimeDataPath, RuntimePath + "/settings.json")));
return m_InitializationOperation;
}
public AsyncOperationHandle<IResourceLocator> LoadContentCatalogAsync(string catalogPath, string providerSuffix = null)
{
var catalogLoc = new ResourceLocationBase(catalogPath, catalogPath, typeof(JsonAssetProvider).FullName, typeof(IResourceLocator));
if (!InitializationOperation.IsDone)
return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadContentCatalogAsync(catalogPath, providerSuffix));
return Initialization.InitializationOperation.LoadContentCatalog(this, catalogLoc, providerSuffix);
}
public AsyncOperationHandle<IResourceLocator> InitializationOperation
{
get
{
if (!m_InitializationOperation.IsValid())
InitializeAsync();
return m_InitializationOperation;
}
}
AsyncOperationHandle<SceneInstance> TrackHandle(AsyncOperationHandle<SceneInstance> handle)
{
handle.Completed += m_OnSceneHandleCompleteAction;
return handle;
}
AsyncOperationHandle<TObject> TrackHandle<TObject>(AsyncOperationHandle<TObject> handle)
{
handle.CompletedTypeless += m_OnHandleCompleteAction;
return handle;
}
AsyncOperationHandle TrackHandle(AsyncOperationHandle handle)
{
handle.Completed += m_OnHandleCompleteAction;
return handle;
}
internal void ClearTrackHandles()
{
m_resultToHandle.Clear();
}
public AsyncOperationHandle<TObject> LoadAssetAsync<TObject>(IResourceLocation location)
{
return TrackHandle(ResourceManager.ProvideResource<TObject>(location));
}
AsyncOperationHandle<TObject> LoadAssetWithChain<TObject>(object key)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadAssetAsync<TObject>(key));
}
public AsyncOperationHandle<TObject> LoadAssetAsync<TObject>(object key)
{
if (!InitializationOperation.IsDone)
return LoadAssetWithChain<TObject>(key);
key = EvaluateKey(key);
IList<IResourceLocation> locs;
var t = typeof(TObject);
if (t.IsArray)
t = t.GetElementType();
else if (t.IsGenericType && typeof(IList<>) == t.GetGenericTypeDefinition())
t = t.GetGenericArguments()[0];
for (int i = 0; i < m_ResourceLocators.Count; i++)
{
if (m_ResourceLocators[i].Locate(key, t, out locs))
{
foreach (var loc in locs)
{
var provider = ResourceManager.GetResourceProvider(typeof(TObject), loc);
if (provider != null)
return TrackHandle(ResourceManager.ProvideResource<TObject>(loc));
}
}
}
return ResourceManager.CreateCompletedOperation<TObject>(default(TObject), new InvalidKeyException(key).Message);
}
class LoadResourceLocationKeyOp : AsyncOperationBase<IList<IResourceLocation>>
{
object m_Key;
IList<IResourceLocation> m_locations;
AddressablesImpl m_Addressables;
Type m_ResourceType;
protected override string DebugName { get { return m_Key.ToString(); } }
public void Init(AddressablesImpl aa, Type t, object key)
{
m_Key = key;
m_ResourceType = t;
m_Addressables = aa;
}
protected override void Execute()
{
m_Addressables.GetResourceLocations(m_Key, m_ResourceType, out m_locations);
if (m_locations == null)
m_locations = new List<IResourceLocation>();
Complete(m_locations, true, string.Empty);
}
}
class LoadResourceLocationKeysOp : AsyncOperationBase<IList<IResourceLocation>>
{
IList<object> m_Key;
Addressables.MergeMode m_MergeMode;
IList<IResourceLocation> m_locations;
AddressablesImpl m_Addressables;
Type m_ResourceType;
protected override string DebugName { get { return "LoadResourceLocationKeysOp"; } }
public void Init(AddressablesImpl aa, Type t, IList<object> key, Addressables.MergeMode mergeMode)
{
m_Key = key;
m_ResourceType = t;
m_MergeMode = mergeMode;
m_Addressables = aa;
}
protected override void Execute()
{
m_Addressables.GetResourceLocations(m_Key, m_ResourceType, m_MergeMode, out m_locations);
if (m_locations == null)
m_locations = new List<IResourceLocation>();
Complete(m_locations, true, string.Empty);
}
}
public AsyncOperationHandle<IList<IResourceLocation>> LoadResourceLocationsAsync(IList<object> keys, Addressables.MergeMode mode, Type type = null)
{
var op = new LoadResourceLocationKeysOp();
op.Init(this, type, keys, mode);
return TrackHandle(ResourceManager.StartOperation(op, InitializationOperation));
}
public AsyncOperationHandle<IList<IResourceLocation>> LoadResourceLocationsAsync(object key, Type type = null)
{
var op = new LoadResourceLocationKeyOp();
op.Init(this, type, key);
return TrackHandle(ResourceManager.StartOperation(op, InitializationOperation));
}
public AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(IList<IResourceLocation> locations, Action<TObject> callback)
{
return TrackHandle(ResourceManager.ProvideResources(locations, callback));
}
AsyncOperationHandle<IList<TObject>> LoadAssetsWithChain<TObject>(IList<object> keys, Action<TObject> callback, Addressables.MergeMode mode)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadAssetsAsync(keys, callback, mode));
}
public AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(IList<object> keys, Action<TObject> callback, Addressables.MergeMode mode)
{
if (!InitializationOperation.IsDone)
return LoadAssetsWithChain(keys, callback, mode);
IList<IResourceLocation> locations;
if (!GetResourceLocations(keys, typeof(TObject), mode, out locations))
return ResourceManager.CreateCompletedOperation<IList<TObject>>(null, new InvalidKeyException(keys).Message);
return LoadAssetsAsync(locations, callback);
}
AsyncOperationHandle<IList<TObject>> LoadAssetsWithChain<TObject>(object key, Action<TObject> callback)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op2 => LoadAssetsAsync(key, callback));
}
public AsyncOperationHandle<IList<TObject>> LoadAssetsAsync<TObject>(object key, Action<TObject> callback)
{
if (!InitializationOperation.IsDone)
return LoadAssetsWithChain(key, callback);
IList<IResourceLocation> locations;
if (!GetResourceLocations(key, typeof(TObject), out locations))
return ResourceManager.CreateCompletedOperation<IList<TObject>>(null, new InvalidKeyException(key).Message);
return LoadAssetsAsync(locations, callback);
}
void OnHandleDestroyed(AsyncOperationHandle handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
m_resultToHandle.Remove(handle.Result);
}
}
void OnSceneHandleCompleted(AsyncOperationHandle<SceneInstance> handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
m_SceneInstances.Add(handle);
if (!m_resultToHandle.ContainsKey(handle.Result))
{
handle.Destroyed += m_OnHandleDestroyedAction;
m_resultToHandle.Add(handle.Result, handle);
}
}
}
void OnHandleCompleted(AsyncOperationHandle handle)
{
if (handle.Status == AsyncOperationStatus.Succeeded)
{
if (!m_resultToHandle.ContainsKey(handle.Result))
{
handle.Destroyed += m_OnHandleDestroyedAction;
m_resultToHandle.Add(handle.Result, handle);
}
}
}
public void Release<TObject>(TObject obj)
{
if (obj == null)
{
LogWarning("Addressables.Release() - trying to release null object.");
return;
}
AsyncOperationHandle handle;
if (m_resultToHandle.TryGetValue(obj, out handle))
Release(handle);
else
{
LogError("Addressables.Release was called on an object that Addressables was not previously aware of. Thus nothing is being released");
}
}
public void Release<TObject>(AsyncOperationHandle<TObject> handle)
{
m_ResourceManager.Release(handle);
}
public void Release(AsyncOperationHandle handle)
{
m_ResourceManager.Release(handle);
}
AsyncOperationHandle<long> GetDownloadSizeWithChain(object key)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op => GetDownloadSizeAsync(key));
}
public AsyncOperationHandle<long> GetDownloadSizeAsync(object key)
{
if (!InitializationOperation.IsDone)
return GetDownloadSizeWithChain(key);
IList<IResourceLocation> locations;
if (typeof(IList<IResourceLocation>).IsAssignableFrom(key.GetType()))
locations = key as IList<IResourceLocation>;
else if (typeof(IResourceLocation).IsAssignableFrom(key.GetType()))
{
locations = new List<IResourceLocation>(1);
locations.Add(key as IResourceLocation);
}
else
{
if (!GetResourceLocations(key, typeof(object), out locations))
return ResourceManager.CreateCompletedOperation<long>(0, new InvalidKeyException(key).Message);
}
var locHash = new HashSet<IResourceLocation>();
foreach (var loc in locations)
{
if (loc.HasDependencies)
{
foreach (var dep in loc.Dependencies)
locHash.Add(dep);
}
}
long size = 0;
foreach (var d in locHash)
{
var sizeData = d.Data as ILocationSizeData;
if (sizeData != null)
size += sizeData.ComputeSize(d);
}
return ResourceManager.CreateCompletedOperation<long>(size, string.Empty);
}
public AsyncOperationHandle DownloadDependenciesAsync(object key)
{
if (!InitializationOperation.IsDone)
return ResourceManager.CreateChainOperation<IList<IAssetBundleResource>>(InitializationOperation, op => DownloadDependenciesAsync(key).Convert<IList<IAssetBundleResource>>());
IList<IResourceLocation> locations;
if (!GetResourceLocations(key, typeof(object), out locations))
return ResourceManager.CreateCompletedOperation<IList<IAssetBundleResource>>(null, new InvalidKeyException(key).Message);
var locHash = new HashSet<IResourceLocation>();
foreach (var loc in locations)
{
if (loc.HasDependencies)
{
foreach (var dep in loc.Dependencies)
locHash.Add(dep);
}
}
return LoadAssetsAsync<IAssetBundleResource>(new List<IResourceLocation>(locHash), null);
}
public AsyncOperationHandle DownloadDependenciesAsync(IList<IResourceLocation> locations)
{
if (!InitializationOperation.IsDone)
return ResourceManager.CreateChainOperation<IList<IAssetBundleResource>>(InitializationOperation, op => DownloadDependenciesAsync(locations).Convert<IList<IAssetBundleResource>>());
var locHash = new HashSet<IResourceLocation>();
foreach (var loc in locations)
{
if (loc.HasDependencies)
{
foreach (var dep in loc.Dependencies)
locHash.Add(dep);
}
}
return LoadAssetsAsync<IAssetBundleResource>(new List<IResourceLocation>(locHash), null);
}
public AsyncOperationHandle DownloadDependenciesAsync(IList<object> keys, Addressables.MergeMode mode)
{
if (!InitializationOperation.IsDone)
return ResourceManager.CreateChainOperation<IList<IAssetBundleResource>>(InitializationOperation, op => DownloadDependenciesAsync(keys, mode).Convert<IList<IAssetBundleResource>>());
IList<IResourceLocation> locations;
if (!GetResourceLocations(keys, typeof(object), mode, out locations))
return ResourceManager.CreateCompletedOperation<IList<IAssetBundleResource>>(null, new InvalidKeyException(keys).Message);
var locHash = new HashSet<IResourceLocation>();
foreach (var loc in locations)
{
if (loc.HasDependencies)
{
foreach (var dep in loc.Dependencies)
locHash.Add(dep);
}
}
return LoadAssetsAsync<IAssetBundleResource>(new List<IResourceLocation>(locHash), null);
}
public AsyncOperationHandle<GameObject> InstantiateAsync(IResourceLocation location, Transform parent = null, bool instantiateInWorldSpace = false, bool trackHandle = true)
{
return InstantiateAsync(location, new InstantiationParameters(parent, instantiateInWorldSpace), trackHandle);
}
public AsyncOperationHandle<GameObject> InstantiateAsync(IResourceLocation location, Vector3 position, Quaternion rotation, Transform parent = null, bool trackHandle = true)
{
return InstantiateAsync(location, new InstantiationParameters(position, rotation, parent), trackHandle);
}
public AsyncOperationHandle<GameObject> InstantiateAsync(object key, Transform parent = null, bool instantiateInWorldSpace = false, bool trackHandle = true)
{
return InstantiateAsync(key, new InstantiationParameters(parent, instantiateInWorldSpace), trackHandle);
}
public AsyncOperationHandle<GameObject> InstantiateAsync(object key, Vector3 position, Quaternion rotation, Transform parent = null, bool trackHandle = true)
{
return InstantiateAsync(key, new InstantiationParameters(position, rotation, parent), trackHandle);
}
AsyncOperationHandle<GameObject> InstantiateWithChain(object key, InstantiationParameters instantiateParameters, bool trackHandle = true)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op => InstantiateAsync(key, instantiateParameters, trackHandle));
}
public AsyncOperationHandle<GameObject> InstantiateAsync(object key, InstantiationParameters instantiateParameters, bool trackHandle = true)
{
if (!InitializationOperation.IsDone)
return InstantiateWithChain(key, instantiateParameters, trackHandle);
key = EvaluateKey(key);
IList<IResourceLocation> locs;
for (int i = 0; i < m_ResourceLocators.Count; i++)
{
if (m_ResourceLocators[i].Locate(key, typeof(GameObject), out locs))
return InstantiateAsync(locs[0], instantiateParameters, trackHandle);
}
return ResourceManager.CreateCompletedOperation<GameObject>(null, new InvalidKeyException(key).Message);
}
public AsyncOperationHandle<GameObject> InstantiateAsync(IResourceLocation location, InstantiationParameters instantiateParameters, bool trackHandle = true)
{
var opHandle = ResourceManager.ProvideInstance(InstanceProvider, location, instantiateParameters);
if (!trackHandle)
return opHandle;
opHandle.CompletedTypeless += m_OnHandleCompleteAction;
return opHandle;
}
public bool ReleaseInstance(GameObject instance)
{
if (instance == null)
{
LogWarning("Addressables.ReleaseInstance() - trying to release null object.");
return false;
}
AsyncOperationHandle handle;
if (m_resultToHandle.TryGetValue(instance, out handle))
Release(handle);
else
return false;
return true;
}
AsyncOperationHandle<SceneInstance> LoadSceneWithChain(object key, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100)
{
return ResourceManager.CreateChainOperation(InitializationOperation, op => LoadSceneAsync(key, loadMode, activateOnLoad, priority));
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(object key, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100)
{
if (!InitializationOperation.IsDone)
return LoadSceneWithChain(key, loadMode, activateOnLoad, priority);
IList<IResourceLocation> locations;
if (!GetResourceLocations(key, typeof(SceneInstance), out locations))
return ResourceManager.CreateCompletedOperation<SceneInstance>(default(SceneInstance), new InvalidKeyException(key).Message);
return LoadSceneAsync(locations[0], loadMode, activateOnLoad, priority);
}
public AsyncOperationHandle<SceneInstance> LoadSceneAsync(IResourceLocation location, LoadSceneMode loadMode = LoadSceneMode.Single, bool activateOnLoad = true, int priority = 100)
{
return TrackHandle(ResourceManager.ProvideScene(SceneProvider, location, loadMode, activateOnLoad, priority));
}
public AsyncOperationHandle<SceneInstance> UnloadSceneAsync(SceneInstance scene, bool autoReleaseHandle = true)
{
AsyncOperationHandle handle;
if (!m_resultToHandle.TryGetValue(scene, out handle))
{
var msg = string.Format("Addressables.UnloadSceneAsync() - Cannot find handle for scene {0}", scene);
LogWarning(msg);
return ResourceManager.CreateCompletedOperation<SceneInstance>(scene, msg);
}
return UnloadSceneAsync(handle, autoReleaseHandle);
}
public AsyncOperationHandle<SceneInstance> UnloadSceneAsync(AsyncOperationHandle handle, bool autoReleaseHandle = true)
{
return UnloadSceneAsync(handle.Convert<SceneInstance>(), autoReleaseHandle);
}
public AsyncOperationHandle<SceneInstance> UnloadSceneAsync(AsyncOperationHandle<SceneInstance> handle, bool autoReleaseHandle = true)
{
var relOp = ResourceManager.ReleaseScene(SceneProvider, handle);
if (autoReleaseHandle)
relOp.Completed += op => Release(op);
return relOp;
}
private object EvaluateKey(object obj)
{
if (obj is IKeyEvaluator)
return (obj as IKeyEvaluator).RuntimeKey;
return obj;
}
}
}